langtools/test/tools/javac/api/TestClientCodeWrapper.java
changeset 9071 88cd61b4e5aa
child 9744 3e48977e539d
equal deleted inserted replaced
9070:f847fe5cad3d 9071:88cd61b4e5aa
       
     1 /*
       
     2  * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 6437138 6482554
       
    27  * @summary JSR 199: Compiler doesn't diagnose crash in user code
       
    28  * @library ../lib
       
    29  * @build JavacTestingAbstractProcessor TestClientCodeWrapper
       
    30  * @run main TestClientCodeWrapper
       
    31  */
       
    32 
       
    33 import java.io.*;
       
    34 import java.lang.reflect.Method;
       
    35 import java.net.URI;
       
    36 import java.util.*;
       
    37 import javax.annotation.processing.*;
       
    38 import javax.lang.model.*;
       
    39 import javax.lang.model.element.*;
       
    40 import javax.tools.*;
       
    41 import com.sun.source.util.*;
       
    42 import com.sun.tools.javac.api.*;
       
    43 import javax.tools.JavaFileObject.Kind;
       
    44 
       
    45 public class TestClientCodeWrapper extends JavacTestingAbstractProcessor {
       
    46     public static void main(String... args) throws Exception {
       
    47         new TestClientCodeWrapper().run();
       
    48     }
       
    49 
       
    50     /**
       
    51      * Run a series of compilations, each with a different user-provided object
       
    52      * configured to throw an exception when a specific method is invoked.
       
    53      * Then, verify the exception is thrown as expected.
       
    54      *
       
    55      * Some methods are not invoked from the compiler, and are excluded from the test.
       
    56      */
       
    57     void run() throws Exception {
       
    58         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
       
    59         defaultFileManager = compiler.getStandardFileManager(null, null, null);
       
    60 
       
    61         for (Method m: getMethodsExcept(JavaFileManager.class, "close", "getJavaFileForInput")) {
       
    62             test(m);
       
    63         }
       
    64 
       
    65         for (Method m: getMethodsExcept(FileObject.class, "delete")) {
       
    66             test(m);
       
    67         }
       
    68 
       
    69         for (Method m: getMethods(JavaFileObject.class)) {
       
    70             test(m);
       
    71         }
       
    72 
       
    73         for (Method m: getMethodsExcept(Processor.class, "getCompletions")) {
       
    74             test(m);
       
    75         }
       
    76 
       
    77         for (Method m: DiagnosticListener.class.getDeclaredMethods()) {
       
    78             test(m);
       
    79         }
       
    80 
       
    81         for (Method m: TaskListener.class.getDeclaredMethods()) {
       
    82             test(m);
       
    83         }
       
    84 
       
    85         if (errors > 0)
       
    86             throw new Exception(errors + " errors occurred");
       
    87     }
       
    88 
       
    89     /** Get a sorted set of the methods declared on a class. */
       
    90     Set<Method> getMethods(Class<?> clazz) {
       
    91         return getMethodsExcept(clazz, new String[0]);
       
    92     }
       
    93 
       
    94     /** Get a sorted set of the methods declared on a class, excluding
       
    95      *  specified methods by name. */
       
    96     Set<Method> getMethodsExcept(Class<?> clazz, String... exclude) {
       
    97         Set<Method> methods = new TreeSet<Method>(new Comparator<Method>() {
       
    98             public int compare(Method m1, Method m2) {
       
    99                 return m1.toString().compareTo(m2.toString());
       
   100             }
       
   101         });
       
   102         Set<String> e = new HashSet<String>(Arrays.asList(exclude));
       
   103         for (Method m: clazz.getDeclaredMethods()) {
       
   104             if (!e.contains(m.getName()))
       
   105                 methods.add(m);
       
   106         }
       
   107         return methods;
       
   108     }
       
   109 
       
   110     /**
       
   111      * Test a method in a user supplied component, to verify javac's handling
       
   112      * of any exceptions thrown by that method.
       
   113      */
       
   114     void test(Method m) throws Exception {
       
   115         testNum++;
       
   116 
       
   117         File extDirs = new File("empty-extdirs");
       
   118         extDirs.mkdirs();
       
   119 
       
   120         File testClasses = new File("test" + testNum);
       
   121         testClasses.mkdirs();
       
   122         defaultFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(testClasses));
       
   123 
       
   124         System.err.println("test " + testNum + ": "
       
   125                 + m.getDeclaringClass().getSimpleName() + "." + m.getName());
       
   126 
       
   127         StringWriter sw = new StringWriter();
       
   128         PrintWriter pw = new PrintWriter(sw);
       
   129 
       
   130         List<String> javacOptions = Arrays.asList(
       
   131                 "-extdirs", extDirs.getPath(), // for use by filemanager handleOption
       
   132                 "-processor", TestClientCodeWrapper.class.getName()
       
   133                 );
       
   134 
       
   135         List<String> classes = Collections.emptyList();
       
   136 
       
   137         JavacTool tool = JavacTool.create();
       
   138         try {
       
   139             JavacTask task = tool.getTask(pw,
       
   140                     getFileManager(m, defaultFileManager),
       
   141                     getDiagnosticListener(m, pw),
       
   142                     javacOptions,
       
   143                     classes,
       
   144                     getCompilationUnits(m));
       
   145 
       
   146             if (isDeclaredIn(m, Processor.class))
       
   147                 task.setProcessors(getProcessors(m));
       
   148 
       
   149             if (isDeclaredIn(m, TaskListener.class))
       
   150                 task.setTaskListener(getTaskListener(m, pw));
       
   151 
       
   152             boolean ok = task.call();
       
   153             error("compilation " + (ok ? "succeeded" : "failed") + " unexpectedly");
       
   154         } catch (RuntimeException e) {
       
   155             System.err.println("caught " + e);
       
   156             if (e.getClass() == RuntimeException.class) {
       
   157                 Throwable cause = e.getCause();
       
   158                 if (cause instanceof UserError) {
       
   159                     String expect = m.getName();
       
   160                     String found = cause.getMessage();
       
   161                     checkEqual("exception messaqe", expect, found);
       
   162                 } else {
       
   163                     cause.printStackTrace(System.err);
       
   164                     error("Unexpected exception: " + cause);
       
   165                 }
       
   166             } else {
       
   167                 e.printStackTrace(System.err);
       
   168                 error("Unexpected exception: " + e);
       
   169             }
       
   170         }
       
   171 
       
   172         pw.close();
       
   173         String out = sw.toString();
       
   174         System.err.println(out);
       
   175     }
       
   176 
       
   177     /** Get a file manager to use for the test compilation. */
       
   178     JavaFileManager getFileManager(Method m, JavaFileManager defaultFileManager) {
       
   179         return isDeclaredIn(m, JavaFileManager.class, FileObject.class, JavaFileObject.class)
       
   180                 ? new UserFileManager(m, defaultFileManager)
       
   181                 : defaultFileManager;
       
   182     }
       
   183 
       
   184     /** Get a diagnostic listener to use for the test compilation. */
       
   185     DiagnosticListener<JavaFileObject> getDiagnosticListener(Method m, PrintWriter out) {
       
   186         return isDeclaredIn(m, DiagnosticListener.class)
       
   187                 ? new UserDiagnosticListener(m, out)
       
   188                 : null;
       
   189     }
       
   190 
       
   191     /** Get a set of file objects to use for the test compilation. */
       
   192     Iterable<? extends JavaFileObject> getCompilationUnits(Method m) {
       
   193         File testSrc = new File(System.getProperty("test.src"));
       
   194         File thisSrc = new File(testSrc, TestClientCodeWrapper.class.getName() + ".java");
       
   195         Iterable<? extends JavaFileObject> files = defaultFileManager.getJavaFileObjects(thisSrc);
       
   196         if (isDeclaredIn(m, FileObject.class, JavaFileObject.class))
       
   197             return Arrays.asList(new UserFileObject(m, files.iterator().next()));
       
   198         else
       
   199             return files;
       
   200     }
       
   201 
       
   202     /** Get a set of annotation processors to use for the test compilation. */
       
   203     Iterable<? extends Processor> getProcessors(Method m) {
       
   204         return Arrays.asList(new UserProcessor(m));
       
   205     }
       
   206 
       
   207     /** Get a task listener to use for the test compilation. */
       
   208     TaskListener getTaskListener(Method m, PrintWriter out) {
       
   209         return new UserTaskListener(m, out);
       
   210     }
       
   211 
       
   212     /** Check if two values are .equal, and report an error if not. */
       
   213     <T> void checkEqual(String label, T expect, T found) {
       
   214         if (!expect.equals(found))
       
   215             error("Unexpected value for " + label + ": " + found + "; expected: " + expect);
       
   216     }
       
   217 
       
   218     /** Report an error. */
       
   219     void error(String msg) {
       
   220         System.err.println("Error: " + msg);
       
   221         errors++;
       
   222     }
       
   223 
       
   224     /** Check if a method is declared in any of a set of classes */
       
   225     static boolean isDeclaredIn(Method m, Class<?>... classes) {
       
   226         Class<?> dc = m.getDeclaringClass();
       
   227         for (Class<?> c: classes) {
       
   228             if (c == dc) return true;
       
   229         }
       
   230         return false;
       
   231     }
       
   232 
       
   233     /** Throw an intentional error if the method has a given name. */
       
   234     static void throwUserExceptionIfNeeded(Method m, String name) {
       
   235         if (m != null && m.getName().equals(name))
       
   236             throw new UserError(name);
       
   237     }
       
   238 
       
   239     StandardJavaFileManager defaultFileManager;
       
   240     int testNum;
       
   241     int errors;
       
   242 
       
   243     //--------------------------------------------------------------------------
       
   244 
       
   245     /**
       
   246      * Processor used to trigger use of methods not normally used by javac.
       
   247      */
       
   248     @Override
       
   249     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   250         boolean firstRound = false;
       
   251         for (Element e: roundEnv.getRootElements()) {
       
   252             if (e.getSimpleName().contentEquals(TestClientCodeWrapper.class.getSimpleName()))
       
   253                 firstRound = true;
       
   254         }
       
   255         if (firstRound) {
       
   256             try {
       
   257                 FileObject f1 = filer.getResource(StandardLocation.CLASS_PATH, "",
       
   258                     TestClientCodeWrapper.class.getName() + ".java");
       
   259                 f1.openInputStream().close();
       
   260                 f1.openReader(false).close();
       
   261 
       
   262                 FileObject f2 = filer.createResource(
       
   263                         StandardLocation.CLASS_OUTPUT, "", "f2.txt", (Element[]) null);
       
   264                 f2.openOutputStream().close();
       
   265 
       
   266                 FileObject f3 = filer.createResource(
       
   267                         StandardLocation.CLASS_OUTPUT, "", "f3.txt", (Element[]) null);
       
   268                 f3.openWriter().close();
       
   269 
       
   270                 JavaFileObject f4 = filer.createSourceFile("f4", (Element[]) null);
       
   271                 f4.openWriter().close();
       
   272                 f4.getNestingKind();
       
   273                 f4.getAccessLevel();
       
   274 
       
   275                 messager.printMessage(Diagnostic.Kind.NOTE, "informational note",
       
   276                         roundEnv.getRootElements().iterator().next());
       
   277 
       
   278             } catch (IOException e) {
       
   279                 throw new UserError(e);
       
   280             }
       
   281         }
       
   282         return true;
       
   283     }
       
   284 
       
   285     //--------------------------------------------------------------------------
       
   286 
       
   287     // <editor-fold defaultstate="collapsed" desc="User classes">
       
   288 
       
   289     static class UserError extends Error {
       
   290         private static final long serialVersionUID = 1L;
       
   291         UserError(String msg) {
       
   292             super(msg);
       
   293         }
       
   294         UserError(Throwable t) {
       
   295             super(t);
       
   296         }
       
   297     }
       
   298 
       
   299     static class UserFileManager extends ForwardingJavaFileManager<JavaFileManager> {
       
   300         Method fileManagerMethod;
       
   301         Method fileObjectMethod;
       
   302 
       
   303         UserFileManager(Method m, JavaFileManager delegate) {
       
   304             super(delegate);
       
   305             if (isDeclaredIn(m, JavaFileManager.class)) {
       
   306                 fileManagerMethod = m;
       
   307             } else if (isDeclaredIn(m, FileObject.class, JavaFileObject.class)) {
       
   308                 fileObjectMethod = m;
       
   309             } else
       
   310                 assert false;
       
   311         }
       
   312 
       
   313         @Override
       
   314         public ClassLoader getClassLoader(Location location) {
       
   315             throwUserExceptionIfNeeded(fileManagerMethod, "getClassLoader");
       
   316             return super.getClassLoader(location);
       
   317         }
       
   318 
       
   319         @Override
       
   320         public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
       
   321             throwUserExceptionIfNeeded(fileManagerMethod, "list");
       
   322             return wrap(super.list(location, packageName, kinds, recurse));
       
   323         }
       
   324 
       
   325         @Override
       
   326         public String inferBinaryName(Location location, JavaFileObject file) {
       
   327             throwUserExceptionIfNeeded(fileManagerMethod, "inferBinaryName");
       
   328             return super.inferBinaryName(location, unwrap(file));
       
   329         }
       
   330 
       
   331         @Override
       
   332         public boolean isSameFile(FileObject a, FileObject b) {
       
   333             throwUserExceptionIfNeeded(fileManagerMethod, "isSameFile");
       
   334             return super.isSameFile(unwrap(a), unwrap(b));
       
   335         }
       
   336 
       
   337         @Override
       
   338         public boolean handleOption(String current, Iterator<String> remaining) {
       
   339             throwUserExceptionIfNeeded(fileManagerMethod, "handleOption");
       
   340             return super.handleOption(current, remaining);
       
   341         }
       
   342 
       
   343         @Override
       
   344         public boolean hasLocation(Location location) {
       
   345             throwUserExceptionIfNeeded(fileManagerMethod, "hasLocation");
       
   346             return super.hasLocation(location);
       
   347         }
       
   348 
       
   349         @Override
       
   350         public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
       
   351             throwUserExceptionIfNeeded(fileManagerMethod, "getJavaFileForInput");
       
   352             return wrap(super.getJavaFileForInput(location, className, kind));
       
   353         }
       
   354 
       
   355         @Override
       
   356         public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
       
   357             throwUserExceptionIfNeeded(fileManagerMethod, "getJavaFileForOutput");
       
   358             return wrap(super.getJavaFileForOutput(location, className, kind, sibling));
       
   359         }
       
   360 
       
   361         @Override
       
   362         public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
       
   363             throwUserExceptionIfNeeded(fileManagerMethod, "getFileForInput");
       
   364             return wrap(super.getFileForInput(location, packageName, relativeName));
       
   365         }
       
   366 
       
   367         @Override
       
   368         public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
       
   369             throwUserExceptionIfNeeded(fileManagerMethod, "getFileForOutput");
       
   370             return wrap(super.getFileForOutput(location, packageName, relativeName, sibling));
       
   371         }
       
   372 
       
   373         @Override
       
   374         public void flush() throws IOException {
       
   375             throwUserExceptionIfNeeded(fileManagerMethod, "flush");
       
   376             super.flush();
       
   377         }
       
   378 
       
   379         @Override
       
   380         public void close() throws IOException {
       
   381             throwUserExceptionIfNeeded(fileManagerMethod, "close");
       
   382             super.close();
       
   383         }
       
   384 
       
   385         @Override
       
   386         public int isSupportedOption(String option) {
       
   387             throwUserExceptionIfNeeded(fileManagerMethod, "isSupportedOption");
       
   388             return super.isSupportedOption(option);
       
   389         }
       
   390 
       
   391         public FileObject wrap(FileObject fo) {
       
   392             if (fileObjectMethod == null)
       
   393                 return fo;
       
   394             return new UserFileObject(fileObjectMethod, (JavaFileObject)fo);
       
   395         }
       
   396 
       
   397         FileObject unwrap(FileObject fo) {
       
   398             if (fo instanceof UserFileObject)
       
   399                 return ((UserFileObject) fo).unwrap();
       
   400             else
       
   401                 return fo;
       
   402         }
       
   403 
       
   404         public JavaFileObject wrap(JavaFileObject fo) {
       
   405             if (fileObjectMethod == null)
       
   406                 return fo;
       
   407             return new UserFileObject(fileObjectMethod, fo);
       
   408         }
       
   409 
       
   410         public Iterable<JavaFileObject> wrap(Iterable<? extends JavaFileObject> list) {
       
   411             List<JavaFileObject> wrapped = new ArrayList<JavaFileObject>();
       
   412             for (JavaFileObject fo : list)
       
   413                 wrapped.add(wrap(fo));
       
   414             return Collections.unmodifiableList(wrapped);
       
   415         }
       
   416 
       
   417         JavaFileObject unwrap(JavaFileObject fo) {
       
   418             if (fo instanceof UserFileObject)
       
   419                 return ((UserFileObject) fo).unwrap();
       
   420             else
       
   421                 return fo;
       
   422         }
       
   423     }
       
   424 
       
   425     static class UserFileObject extends ForwardingJavaFileObject<JavaFileObject> {
       
   426         Method method;
       
   427 
       
   428         UserFileObject(Method m, JavaFileObject delegate) {
       
   429             super(delegate);
       
   430             assert isDeclaredIn(m, FileObject.class, JavaFileObject.class);
       
   431             this.method = m;
       
   432         }
       
   433 
       
   434         JavaFileObject unwrap() {
       
   435             return fileObject;
       
   436         }
       
   437 
       
   438         @Override
       
   439         public Kind getKind() {
       
   440             throwUserExceptionIfNeeded(method, "getKind");
       
   441             return super.getKind();
       
   442         }
       
   443 
       
   444         @Override
       
   445         public boolean isNameCompatible(String simpleName, Kind kind) {
       
   446             throwUserExceptionIfNeeded(method, "isNameCompatible");
       
   447             return super.isNameCompatible(simpleName, kind);
       
   448         }
       
   449 
       
   450         @Override
       
   451         public NestingKind getNestingKind() {
       
   452             throwUserExceptionIfNeeded(method, "getNestingKind");
       
   453             return super.getNestingKind();
       
   454         }
       
   455 
       
   456         @Override
       
   457         public Modifier getAccessLevel() {
       
   458             throwUserExceptionIfNeeded(method, "getAccessLevel");
       
   459             return super.getAccessLevel();
       
   460         }
       
   461 
       
   462         @Override
       
   463         public URI toUri() {
       
   464             throwUserExceptionIfNeeded(method, "toUri");
       
   465             return super.toUri();
       
   466         }
       
   467 
       
   468         @Override
       
   469         public String getName() {
       
   470             throwUserExceptionIfNeeded(method, "getName");
       
   471             return super.getName();
       
   472         }
       
   473 
       
   474         @Override
       
   475         public InputStream openInputStream() throws IOException {
       
   476             throwUserExceptionIfNeeded(method, "openInputStream");
       
   477             return super.openInputStream();
       
   478         }
       
   479 
       
   480         @Override
       
   481         public OutputStream openOutputStream() throws IOException {
       
   482             throwUserExceptionIfNeeded(method, "openOutputStream");
       
   483             return super.openOutputStream();
       
   484         }
       
   485 
       
   486         @Override
       
   487         public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
       
   488             throwUserExceptionIfNeeded(method, "openReader");
       
   489             return super.openReader(ignoreEncodingErrors);
       
   490         }
       
   491 
       
   492         @Override
       
   493         public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
       
   494             throwUserExceptionIfNeeded(method, "getCharContent");
       
   495             return super.getCharContent(ignoreEncodingErrors);
       
   496         }
       
   497 
       
   498         @Override
       
   499         public Writer openWriter() throws IOException {
       
   500             throwUserExceptionIfNeeded(method, "openWriter");
       
   501             return super.openWriter();
       
   502         }
       
   503 
       
   504         @Override
       
   505         public long getLastModified() {
       
   506             throwUserExceptionIfNeeded(method, "getLastModified");
       
   507             return super.getLastModified();
       
   508         }
       
   509 
       
   510         @Override
       
   511         public boolean delete() {
       
   512             throwUserExceptionIfNeeded(method, "delete");
       
   513             return super.delete();
       
   514         }
       
   515 
       
   516     }
       
   517 
       
   518     static class UserProcessor extends JavacTestingAbstractProcessor {
       
   519         Method method;
       
   520 
       
   521         UserProcessor(Method m) {
       
   522             assert isDeclaredIn(m, Processor.class);
       
   523             method = m;
       
   524         }
       
   525 
       
   526         @Override
       
   527         public Set<String> getSupportedOptions() {
       
   528             throwUserExceptionIfNeeded(method, "getSupportedOptions");
       
   529             return super.getSupportedOptions();
       
   530         }
       
   531 
       
   532         @Override
       
   533         public Set<String> getSupportedAnnotationTypes() {
       
   534             throwUserExceptionIfNeeded(method, "getSupportedAnnotationTypes");
       
   535             return super.getSupportedAnnotationTypes();
       
   536         }
       
   537 
       
   538         @Override
       
   539         public SourceVersion getSupportedSourceVersion() {
       
   540             throwUserExceptionIfNeeded(method, "getSupportedSourceVersion");
       
   541             return super.getSupportedSourceVersion();
       
   542         }
       
   543 
       
   544         @Override
       
   545         public void init(ProcessingEnvironment processingEnv) {
       
   546             throwUserExceptionIfNeeded(method, "init");
       
   547             super.init(processingEnv);
       
   548         }
       
   549 
       
   550         @Override
       
   551         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   552             throwUserExceptionIfNeeded(method, "process");
       
   553             return true;
       
   554         }
       
   555 
       
   556         @Override
       
   557         public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
       
   558             throwUserExceptionIfNeeded(method, "getCompletions");
       
   559             return super.getCompletions(element, annotation, member, userText);
       
   560         }
       
   561     }
       
   562 
       
   563     static class UserDiagnosticListener implements DiagnosticListener<JavaFileObject> {
       
   564         Method method;
       
   565         PrintWriter out;
       
   566 
       
   567         UserDiagnosticListener(Method m, PrintWriter out) {
       
   568             assert isDeclaredIn(m, DiagnosticListener.class);
       
   569             this.method = m;
       
   570             this.out = out;
       
   571         }
       
   572 
       
   573         @Override
       
   574         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
       
   575             throwUserExceptionIfNeeded(method, "report");
       
   576             out.println("report: " + diagnostic);
       
   577         }
       
   578     }
       
   579 
       
   580     static class UserTaskListener implements TaskListener {
       
   581         Method method;
       
   582         PrintWriter out;
       
   583 
       
   584         UserTaskListener(Method m, PrintWriter out) {
       
   585             assert isDeclaredIn(m, TaskListener.class);
       
   586             this.method = m;
       
   587             this.out = out;
       
   588         }
       
   589 
       
   590         @Override
       
   591         public void started(TaskEvent e) {
       
   592             throwUserExceptionIfNeeded(method, "started");
       
   593             out.println("started: " + e);
       
   594         }
       
   595 
       
   596         @Override
       
   597         public void finished(TaskEvent e) {
       
   598             throwUserExceptionIfNeeded(method, "finished");
       
   599             out.println("finished: " + e);
       
   600         }
       
   601     }
       
   602 
       
   603     // </editor-fold>
       
   604 }