7093891: support multiple task listeners
authorjjg
Tue, 28 Feb 2012 10:33:49 -0800
changeset 12016 1990493b64db
parent 12015 0c9d42e88feb
child 12017 15651eb0c1d6
7093891: support multiple task listeners Reviewed-by: darcy, mcimadamore
langtools/src/share/classes/com/sun/source/util/JavacTask.java
langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java
langtools/src/share/classes/com/sun/tools/javac/api/ClientCodeWrapper.java
langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java
langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java
langtools/src/share/classes/com/sun/tools/javac/api/MultiTaskListener.java
langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java
langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
langtools/test/tools/javac/api/taskListeners/TestSimpleAddRemove.java
langtools/test/tools/javac/processing/loader/testClose/TestClose.java
--- a/langtools/src/share/classes/com/sun/source/util/JavacTask.java	Tue Feb 28 10:25:53 2012 -0800
+++ b/langtools/src/share/classes/com/sun/source/util/JavacTask.java	Tue Feb 28 10:33:49 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,9 +25,9 @@
 
 package com.sun.source.util;
 
-import com.sun.source.tree.CompilationUnitTree;
-import com.sun.source.tree.Tree;
 import java.io.IOException;
+
+import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.Element;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Elements;
@@ -35,6 +35,12 @@
 import javax.tools.JavaCompiler.CompilationTask;
 import javax.tools.JavaFileObject;
 
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.api.BasicJavacTask;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
+
 /**
  * Provides access to functionality specific to the JDK Java Compiler, javac.
  *
@@ -45,10 +51,29 @@
 public abstract class JavacTask implements CompilationTask {
 
     /**
+     * Get the {@code JavacTask} for a {@code ProcessingEnvironment}.
+     * If the compiler is being invoked using a
+     * {@link javax.tools.JavaCompiler.CompilationTask CompilationTask},
+     * then that task will be returned.
+     * @param processingEnvironment
+     * @return the {@code JavacTask} for a {@code ProcessingEnvironment}
+     * @since 1.8
+     */
+    public static JavacTask instance(ProcessingEnvironment processingEnvironment) {
+        if (!processingEnvironment.getClass().getName().equals(
+                "com.sun.tools.javac.processing.JavacProcessingEnvironment"))
+            throw new IllegalArgumentException();
+        Context c = ((JavacProcessingEnvironment) processingEnvironment).getContext();
+        JavacTask t = c.get(JavacTask.class);
+        return (t != null) ? t : new BasicJavacTask(c, true);
+    }
+
+    /**
      * Parse the specified files returning a list of abstract syntax trees.
      *
      * @return a list of abstract syntax trees
      * @throws IOException if an unhandled I/O error occurred in the compiler.
+     * @throws IllegalStateException if the operation cannot be performed at this time.
      */
     public abstract Iterable<? extends CompilationUnitTree> parse()
         throws IOException;
@@ -58,6 +83,7 @@
      *
      * @return a list of elements that were analyzed
      * @throws IOException if an unhandled I/O error occurred in the compiler.
+     * @throws IllegalStateException if the operation cannot be performed at this time.
      */
     public abstract Iterable<? extends Element> analyze() throws IOException;
 
@@ -66,17 +92,51 @@
      *
      * @return a list of files that were generated
      * @throws IOException if an unhandled I/O error occurred in the compiler.
+     * @throws IllegalStateException if the operation cannot be performed at this time.
      */
     public abstract Iterable<? extends JavaFileObject> generate() throws IOException;
 
     /**
-     * The specified listener will receive events describing the progress of
-     * this compilation task.
+     * The specified listener will receive notification of events
+     * describing the progress of this compilation task.
+     *
+     * If another listener is receiving notifications as a result of a prior
+     * call of this method, then that listener will no longer receive notifications.
+     *
+     * Informally, this method is equivalent to calling {@code removeTaskListener} for
+     * any listener that has been previously set, followed by {@code addTaskListener}
+     * for the new listener.
+     *
+     * @throws IllegalStateException if the specified listener has already been added.
      */
     public abstract void setTaskListener(TaskListener taskListener);
 
     /**
+     * The specified listener will receive notification of events
+     * describing the progress of this compilation task.
+     *
+     * This method may be called at any time before or during the compilation.
+     *
+     * @throws IllegalStateException if the specified listener has already been added.
+     * @since 1.8
+     */
+    public abstract void addTaskListener(TaskListener taskListener);
+
+    /**
+     * The specified listener will no longer receive notification of events
+     * describing the progress of this compilation task.
+     *
+     * This method may be called at any time before or during the compilation.
+     *
+     * @since 1.8
+     */
+    public abstract void removeTaskListener(TaskListener taskListener);
+
+    /**
      * Get a type mirror of the tree node determined by the specified path.
+     * This method has been superceded by methods on
+     * {@link com.sun.source.util.Trees Trees}.
+     * @see com.sun.source.util.Trees#getTypeMirror
      */
     public abstract TypeMirror getTypeMirror(Iterable<? extends Tree> path);
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java	Tue Feb 28 10:33:49 2012 -0800
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javac.api;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.JavaFileObject;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.model.JavacTypes;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.Context;
+import java.util.Collection;
+
+/**
+ * Provides basic functionality for implementations of JavacTask.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own
+ * risk.  This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ */
+public class BasicJavacTask extends JavacTask {
+    protected Context context;
+    private TaskListener taskListener;
+
+    public BasicJavacTask(Context c, boolean register) {
+        context = c;
+        if (register)
+            context.put(JavacTask.class, this);
+    }
+
+    @Override
+    public Iterable<? extends CompilationUnitTree> parse() throws IOException {
+        throw new IllegalStateException();
+    }
+
+    @Override
+    public Iterable<? extends Element> analyze() throws IOException {
+        throw new IllegalStateException();
+    }
+
+    @Override
+    public Iterable<? extends JavaFileObject> generate() throws IOException {
+        throw new IllegalStateException();
+    }
+
+    @Override
+    public void setTaskListener(TaskListener tl) {
+        MultiTaskListener mtl = MultiTaskListener.instance(context);
+        if (taskListener != null)
+            mtl.remove(taskListener);
+        if (tl != null)
+            mtl.add(tl);
+        taskListener = tl;
+    }
+
+    @Override
+    public void addTaskListener(TaskListener taskListener) {
+        MultiTaskListener mtl = MultiTaskListener.instance(context);
+        mtl.add(taskListener);
+    }
+
+    @Override
+    public void removeTaskListener(TaskListener taskListener) {
+        MultiTaskListener mtl = MultiTaskListener.instance(context);
+        mtl.remove(taskListener);
+    }
+
+    public Collection<TaskListener> getTaskListeners() {
+        MultiTaskListener mtl = MultiTaskListener.instance(context);
+        return mtl.getTaskListeners();
+    }
+
+    @Override
+    public TypeMirror getTypeMirror(Iterable<? extends Tree> path) {
+        // TODO: Should complete attribution if necessary
+        Tree last = null;
+        for (Tree node : path)
+            last = node;
+        return ((JCTree)last).type;
+    }
+
+    @Override
+    public Elements getElements() {
+        return JavacElements.instance(context);
+    }
+
+    @Override
+    public Types getTypes() {
+        return JavacTypes.instance(context);
+    }
+
+    public void setProcessors(Iterable<? extends Processor> processors) {
+        throw new IllegalStateException();
+    }
+
+    public void setLocale(Locale locale) {
+        throw new IllegalStateException();
+    }
+
+    public Boolean call() {
+        throw new IllegalStateException();
+    }
+
+}
--- a/langtools/src/share/classes/com/sun/tools/javac/api/ClientCodeWrapper.java	Tue Feb 28 10:25:53 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/ClientCodeWrapper.java	Tue Feb 28 10:33:49 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,8 +31,13 @@
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.Writer;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -41,25 +46,21 @@
 import java.util.Map;
 import java.util.Set;
 
+import javax.lang.model.element.Modifier;
 import javax.lang.model.element.NestingKind;
 import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
 import javax.tools.FileObject;
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileManager.Location;
 import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
 
 import com.sun.source.util.TaskEvent;
 import com.sun.source.util.TaskListener;
 import com.sun.tools.javac.util.ClientCodeException;
 import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.util.JCDiagnostic;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import javax.lang.model.element.Modifier;
-import javax.tools.DiagnosticListener;
-import javax.tools.JavaFileObject.Kind;
 
 /**
  *  Wrap objects to enable unchecked exceptions to be caught and handled.
@@ -160,6 +161,20 @@
         return new WrappedTaskListener(tl);
     }
 
+    TaskListener unwrap(TaskListener l) {
+        if (l instanceof WrappedTaskListener)
+            return ((WrappedTaskListener) l).clientTaskListener;
+        else
+            return l;
+    }
+
+    Collection<TaskListener> unwrap(Collection<? extends TaskListener> listeners) {
+        Collection<TaskListener> c = new ArrayList<TaskListener>(listeners.size());
+        for (TaskListener l: listeners)
+            c.add(unwrap(l));
+        return c;
+    }
+
     @SuppressWarnings("unchecked")
     private <T> Diagnostic<T> unwrap(final Diagnostic<T> diagnostic) {
         if (diagnostic instanceof JCDiagnostic) {
@@ -181,6 +196,10 @@
         return trusted;
     }
 
+    private String wrappedToString(Class<?> wrapperClass, Object wrapped) {
+        return wrapperClass.getSimpleName() + "[" + wrapped + "]";
+    }
+
     // <editor-fold defaultstate="collapsed" desc="Wrapper classes">
 
     // FIXME: all these classes should be converted to use multi-catch when
@@ -361,6 +380,11 @@
                 throw new ClientCodeException(e);
             }
         }
+
+        @Override
+        public String toString() {
+            return wrappedToString(getClass(), clientJavaFileManager);
+        }
     }
 
     protected class WrappedFileObject implements FileObject {
@@ -486,6 +510,11 @@
                 throw new ClientCodeException(e);
             }
         }
+
+        @Override
+        public String toString() {
+            return wrappedToString(getClass(), clientFileObject);
+        }
     }
 
     protected class WrappedJavaFileObject extends WrappedFileObject implements JavaFileObject {
@@ -544,6 +573,11 @@
                 throw new ClientCodeException(e);
             }
         }
+
+        @Override
+        public String toString() {
+            return wrappedToString(getClass(), clientFileObject);
+        }
     }
 
     protected class WrappedDiagnosticListener<T /*super JavaFileObject*/> implements DiagnosticListener<T> {
@@ -565,6 +599,11 @@
                 throw new ClientCodeException(e);
             }
         }
+
+        @Override
+        public String toString() {
+            return wrappedToString(getClass(), clientDiagnosticListener);
+        }
     }
 
     public class DiagnosticSourceUnwrapper implements Diagnostic<JavaFileObject> {
@@ -610,6 +649,7 @@
             return d.getMessage(locale);
         }
 
+        @Override
         public String toString() {
             return d.toString();
         }
@@ -647,6 +687,11 @@
                 throw new ClientCodeException(e);
             }
         }
+
+        @Override
+        public String toString() {
+            return wrappedToString(getClass(), clientTaskListener);
+        }
     }
 
     // </editor-fold>
--- a/langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java	Tue Feb 28 10:25:53 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java	Tue Feb 28 10:33:49 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -44,6 +44,7 @@
 import com.sun.tools.javac.comp.*;
 import com.sun.tools.javac.file.JavacFileManager;
 import com.sun.tools.javac.main.*;
+import com.sun.tools.javac.main.JavaCompiler;
 import com.sun.tools.javac.model.*;
 import com.sun.tools.javac.parser.Parser;
 import com.sun.tools.javac.parser.ParserFactory;
@@ -51,7 +52,6 @@
 import com.sun.tools.javac.tree.JCTree.*;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.main.JavaCompiler;
 
 /**
  * Provides access to functionality specific to the JDK Java Compiler, javac.
@@ -64,18 +64,16 @@
  * @author Peter von der Ah&eacute;
  * @author Jonathan Gibbons
  */
-public class JavacTaskImpl extends JavacTask {
+public class JavacTaskImpl extends BasicJavacTask {
     private ClientCodeWrapper ccw;
     private Main compilerMain;
     private JavaCompiler compiler;
     private Locale locale;
     private String[] args;
     private String[] classNames;
-    private Context context;
     private List<JavaFileObject> fileObjects;
     private Map<JavaFileObject, JCCompilationUnit> notYetEntered;
     private ListBuffer<Env<AttrContext>> genList;
-    private TaskListener taskListener;
     private AtomicBoolean used = new AtomicBoolean();
     private Iterable<? extends Processor> processors;
 
@@ -86,6 +84,7 @@
                 String[] classNames,
                 Context context,
                 List<JavaFileObject> fileObjects) {
+        super(null, false);
         this.ccw = ClientCodeWrapper.instance(context);
         this.compilerMain = compilerMain;
         this.args = args;
@@ -190,11 +189,7 @@
     }
 
     private void initContext() {
-        context.put(JavacTaskImpl.class, this);
-        if (context.get(TaskListener.class) != null)
-            context.put(TaskListener.class, (TaskListener)null);
-        if (taskListener != null)
-            context.put(TaskListener.class, ccw.wrap(taskListener));
+        context.put(JavacTask.class, this);
         //initialize compiler's default locale
         context.put(Locale.class, locale);
     }
@@ -224,10 +219,6 @@
         return fm.getRegularFile(file);
     }
 
-    public void setTaskListener(TaskListener taskListener) {
-        this.taskListener = taskListener;
-    }
-
     /**
      * Parse the specified files returning a list of abstract syntax trees.
      *
--- a/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java	Tue Feb 28 10:25:53 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java	Tue Feb 28 10:33:49 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
 package com.sun.tools.javac.api;
 
 import java.io.IOException;
-import java.util.Map;
+
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
@@ -44,13 +44,14 @@
 import com.sun.source.tree.CompilationUnitTree;
 import com.sun.source.tree.Scope;
 import com.sun.source.tree.Tree;
+import com.sun.source.util.JavacTask;
 import com.sun.source.util.SourcePositions;
 import com.sun.source.util.TreePath;
 import com.sun.source.util.Trees;
 import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 import com.sun.tools.javac.code.Symbol.TypeSymbol;
-import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Type.UnionClassType;
 import com.sun.tools.javac.comp.Attr;
 import com.sun.tools.javac.comp.AttrContext;
@@ -61,8 +62,8 @@
 import com.sun.tools.javac.model.JavacElements;
 import com.sun.tools.javac.parser.EndPosTable;
 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.JCTree.*;
-import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.TreeCopier;
 import com.sun.tools.javac.tree.TreeInfo;
 import com.sun.tools.javac.tree.TreeMaker;
@@ -95,12 +96,14 @@
     private JavacElements elements;
     private JavacTaskImpl javacTaskImpl;
 
+    // called reflectively from Trees.instance(CompilationTask task)
     public static JavacTrees instance(JavaCompiler.CompilationTask task) {
         if (!(task instanceof JavacTaskImpl))
             throw new IllegalArgumentException();
         return instance(((JavacTaskImpl)task).getContext());
     }
 
+    // called reflectively from Trees.instance(ProcessingEnvironment env)
     public static JavacTrees instance(ProcessingEnvironment env) {
         if (!(env instanceof JavacProcessingEnvironment))
             throw new IllegalArgumentException();
@@ -131,7 +134,10 @@
         resolve = Resolve.instance(context);
         treeMaker = TreeMaker.instance(context);
         memberEnter = MemberEnter.instance(context);
-        javacTaskImpl = context.get(JavacTaskImpl.class);
+
+        JavacTask t = context.get(JavacTask.class);
+        if (t instanceof JavacTaskImpl)
+            javacTaskImpl = (JavacTaskImpl) t;
     }
 
     public SourcePositions getSourcePositions() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/MultiTaskListener.java	Tue Feb 28 10:33:49 2012 -0800
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javac.api;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.util.Context;
+
+/**
+ * TODO.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+public class MultiTaskListener implements TaskListener {
+    /** The context key for the MultiTaskListener. */
+    public static final Context.Key<MultiTaskListener> taskListenerKey =
+        new Context.Key<MultiTaskListener>();
+
+    /** Get the MultiTaskListener instance for this context. */
+    public static MultiTaskListener instance(Context context) {
+        MultiTaskListener instance = context.get(taskListenerKey);
+        if (instance == null)
+            instance = new MultiTaskListener(context);
+        return instance;
+    }
+
+    protected MultiTaskListener(Context context) {
+        context.put(taskListenerKey, this);
+        ccw = ClientCodeWrapper.instance(context);
+    }
+
+    /**
+     * The current set of registered listeners.
+     * This is a mutable reference to an immutable array.
+     */
+    TaskListener[] listeners = { };
+
+    ClientCodeWrapper ccw;
+
+    public Collection<TaskListener> getTaskListeners() {
+        return Arrays.asList(listeners);
+    }
+
+    public boolean isEmpty() {
+        return (listeners.length == 0);
+    }
+
+    public void add(TaskListener listener) {
+        for (TaskListener l: listeners) {
+            if (ccw.unwrap(l) == listener)
+                throw new IllegalStateException();
+        }
+        TaskListener[] newListeners = new TaskListener[listeners.length + 1];
+        System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
+        newListeners[newListeners.length - 1] = ccw.wrap(listener);
+        listeners = newListeners;
+    }
+
+    public void remove(TaskListener listener) {
+        for (int i = 0; i < listeners.length; i++) {
+            if (ccw.unwrap(listeners[i]) == listener) {
+                TaskListener[] newListeners = new TaskListener[listeners.length - 1];
+                System.arraycopy(listeners, 0, newListeners, 0, i);
+                System.arraycopy(listeners, i + 1, newListeners, i, newListeners.length - i);
+                listeners = newListeners;
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void started(TaskEvent e) {
+        // guard against listeners being updated by a listener
+        TaskListener[] ll = this.listeners;
+        for (TaskListener l: ll)
+            l.started(e);
+    }
+
+    @Override
+    public void finished(TaskEvent e) {
+        // guard against listeners being updated by a listener
+        TaskListener[] ll = this.listeners;
+        for (TaskListener l: ll)
+            l.finished(e);
+    }
+
+    @Override
+    public String toString() {
+        return Arrays.toString(listeners);
+    }
+}
--- a/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Tue Feb 28 10:25:53 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Tue Feb 28 10:33:49 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,8 +28,8 @@
 import java.io.*;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
-import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.MissingResourceException;
 import java.util.Queue;
@@ -41,13 +41,13 @@
 
 import javax.annotation.processing.Processor;
 import javax.lang.model.SourceVersion;
+import javax.tools.DiagnosticListener;
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileObject;
-import javax.tools.DiagnosticListener;
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
 
 import com.sun.source.util.TaskEvent;
-import com.sun.source.util.TaskListener;
-
+import com.sun.tools.javac.api.MultiTaskListener;
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Lint.LintCategory;
 import com.sun.tools.javac.code.Symbol.*;
@@ -60,8 +60,6 @@
 import com.sun.tools.javac.tree.JCTree.*;
 import com.sun.tools.javac.util.*;
 import com.sun.tools.javac.util.Log.WriterKind;
-
-import static javax.tools.StandardLocation.CLASS_OUTPUT;
 import static com.sun.tools.javac.main.Option.*;
 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*;
 import static com.sun.tools.javac.util.ListBuffer.lb;
@@ -289,9 +287,9 @@
      */
     protected ParserFactory parserFactory;
 
-    /** Optional listener for progress events
+    /** Broadcasting listener for progress events
      */
-    protected TaskListener taskListener;
+    protected MultiTaskListener taskListener;
 
     /**
      * Annotation processing may require and provide a new instance
@@ -356,7 +354,7 @@
         lower = Lower.instance(context);
         annotate = Annotate.instance(context);
         types = Types.instance(context);
-        taskListener = context.get(TaskListener.class);
+        taskListener = MultiTaskListener.instance(context);
 
         reader.sourceCompleter = this;
 
@@ -592,7 +590,7 @@
             if (verbose) {
                 log.printVerbose("parsing.started", filename);
             }
-            if (taskListener != null) {
+            if (!taskListener.isEmpty()) {
                 TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, filename);
                 taskListener.started(e);
             }
@@ -605,7 +603,7 @@
 
         tree.sourcefile = filename;
 
-        if (content != null && taskListener != null) {
+        if (content != null && !taskListener.isEmpty()) {
             TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, tree);
             taskListener.finished(e);
         }
@@ -751,14 +749,14 @@
             log.useSource(prev);
         }
 
-        if (taskListener != null) {
+        if (!taskListener.isEmpty()) {
             TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
             taskListener.started(e);
         }
 
         enter.complete(List.of(tree), c);
 
-        if (taskListener != null) {
+        if (!taskListener.isEmpty()) {
             TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, tree);
             taskListener.finished(e);
         }
@@ -924,7 +922,7 @@
      */
     public List<JCCompilationUnit> enterTrees(List<JCCompilationUnit> roots) {
         //enter symbols for all files
-        if (taskListener != null) {
+        if (!taskListener.isEmpty()) {
             for (JCCompilationUnit unit: roots) {
                 TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit);
                 taskListener.started(e);
@@ -933,7 +931,7 @@
 
         enter.main(roots);
 
-        if (taskListener != null) {
+        if (!taskListener.isEmpty()) {
             for (JCCompilationUnit unit: roots) {
                 TaskEvent e = new TaskEvent(TaskEvent.Kind.ENTER, unit);
                 taskListener.finished(e);
@@ -1002,7 +1000,7 @@
                 reader.saveParameterNames = true;
                 keepComments = true;
                 genEndPos = true;
-                if (taskListener != null)
+                if (!taskListener.isEmpty())
                     taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
                 log.deferDiagnostics = true;
             } else { // free resources
@@ -1017,7 +1015,7 @@
     }
 
     /**
-     * Process any anotations found in the specifed compilation units.
+     * Process any annotations found in the specified compilation units.
      * @param roots a list of compilation units
      * @return an instance of the compiler in which to complete the compilation
      */
@@ -1176,7 +1174,7 @@
         if (verbose)
             log.printVerbose("checking.attribution", env.enclClass.sym);
 
-        if (taskListener != null) {
+        if (!taskListener.isEmpty()) {
             TaskEvent e = new TaskEvent(TaskEvent.Kind.ANALYZE, env.toplevel, env.enclClass.sym);
             taskListener.started(e);
         }
@@ -1259,7 +1257,7 @@
             }
         }
         finally {
-            if (taskListener != null) {
+            if (!taskListener.isEmpty()) {
                 TaskEvent e = new TaskEvent(TaskEvent.Kind.ANALYZE, env.toplevel, env.enclClass.sym);
                 taskListener.finished(e);
             }
@@ -1440,7 +1438,7 @@
                                + " " + cdef.sym + "]");
             }
 
-            if (taskListener != null) {
+            if (!taskListener.isEmpty()) {
                 TaskEvent e = new TaskEvent(TaskEvent.Kind.GENERATE, env.toplevel, cdef.sym);
                 taskListener.started(e);
             }
@@ -1464,7 +1462,7 @@
                 log.useSource(prev);
             }
 
-            if (taskListener != null) {
+            if (!taskListener.isEmpty()) {
                 TaskEvent e = new TaskEvent(TaskEvent.Kind.GENERATE, env.toplevel, cdef.sym);
                 taskListener.finished(e);
             }
--- a/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Tue Feb 28 10:25:53 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Tue Feb 28 10:33:49 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,34 +25,33 @@
 
 package com.sun.tools.javac.processing;
 
-import java.lang.reflect.*;
-import java.util.*;
-import java.util.regex.*;
-
-import java.net.URL;
 import java.io.Closeable;
 import java.io.File;
 import java.io.PrintWriter;
-import java.io.IOException;
 import java.io.StringWriter;
 import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import java.util.regex.*;
 
 import javax.annotation.processing.*;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.PackageElement;
 import javax.lang.model.util.*;
+import javax.tools.DiagnosticListener;
 import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
 import javax.tools.StandardJavaFileManager;
-import javax.tools.JavaFileObject;
-import javax.tools.DiagnosticListener;
+import static javax.tools.StandardLocation.*;
 
+import com.sun.source.util.JavacTask;
 import com.sun.source.util.TaskEvent;
-import com.sun.source.util.TaskListener;
 import com.sun.tools.javac.api.JavacTaskImpl;
 import com.sun.tools.javac.api.JavacTrees;
+import com.sun.tools.javac.api.MultiTaskListener;
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.file.FSInfo;
@@ -71,19 +70,16 @@
 import com.sun.tools.javac.util.ClientCodeException;
 import com.sun.tools.javac.util.Context;
 import com.sun.tools.javac.util.Convert;
-import com.sun.tools.javac.util.FatalError;
 import com.sun.tools.javac.util.JCDiagnostic;
+import com.sun.tools.javac.util.JavacMessages;
 import com.sun.tools.javac.util.List;
 import com.sun.tools.javac.util.Log;
-import com.sun.tools.javac.util.JavacMessages;
 import com.sun.tools.javac.util.Name;
 import com.sun.tools.javac.util.Names;
 import com.sun.tools.javac.util.Options;
-
-import static javax.tools.StandardLocation.*;
+import static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING;
+import static com.sun.tools.javac.main.Option.*;
 import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag.*;
-import static com.sun.tools.javac.main.Option.*;
-import static com.sun.tools.javac.code.Lint.LintCategory.PROCESSING;
 
 /**
  * Objects of this class hold and manage the state needed to support
@@ -157,6 +153,8 @@
      */
     private JavacMessages messages;
 
+    private MultiTaskListener taskListener;
+
     private Context context;
 
     public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
@@ -185,6 +183,7 @@
         processorOptions = initProcessorOptions(context);
         unmatchedProcessorOptions = initUnmatchedProcessorOptions();
         messages = JavacMessages.instance(context);
+        taskListener = MultiTaskListener.instance(context);
         initProcessorIterator(context, processors);
     }
 
@@ -976,8 +975,7 @@
         void run(boolean lastRound, boolean errorStatus) {
             printRoundInfo(lastRound);
 
-            TaskListener taskListener = context.get(TaskListener.class);
-            if (taskListener != null)
+            if (!taskListener.isEmpty())
                 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
 
             try {
@@ -993,7 +991,7 @@
                     discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles);
                 }
             } finally {
-                if (taskListener != null)
+                if (!taskListener.isEmpty())
                     taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
             }
 
@@ -1051,9 +1049,9 @@
             if (dl != null)
                 next.put(DiagnosticListener.class, dl);
 
-            TaskListener tl = context.get(TaskListener.class);
-            if (tl != null)
-                next.put(TaskListener.class, tl);
+            MultiTaskListener mtl = context.get(MultiTaskListener.taskListenerKey);
+            if (mtl != null)
+                next.put(MultiTaskListener.taskListenerKey, mtl);
 
             FSInfo fsInfo = context.get(FSInfo.class);
             if (fsInfo != null)
@@ -1086,9 +1084,9 @@
             elementUtils.setContext(next);
             typeUtils.setContext(next);
 
-            JavacTaskImpl task = context.get(JavacTaskImpl.class);
+            JavacTaskImpl task = (JavacTaskImpl) context.get(JavacTask.class);
             if (task != null) {
-                next.put(JavacTaskImpl.class, task);
+                next.put(JavacTask.class, task);
                 task.updateContext(next);
             }
 
@@ -1110,8 +1108,6 @@
                                      List<JCCompilationUnit> roots,
                                      List<ClassSymbol> classSymbols,
                                      Iterable<? extends PackageSymbol> pckSymbols) {
-
-        TaskListener taskListener = context.get(TaskListener.class);
         log = Log.instance(context);
 
         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
@@ -1182,7 +1178,7 @@
         // Free resources
         this.close();
 
-        if (taskListener != null)
+        if (!taskListener.isEmpty())
             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
 
         if (errorStatus) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/api/taskListeners/TestSimpleAddRemove.java	Tue Feb 28 10:33:49 2012 -0800
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug     7093891
+ * @summary support multiple task listeners
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.api.JavacTool;
+
+public class TestSimpleAddRemove {
+    enum AddKind {
+        SET_IN_TASK,
+        ADD_IN_TASK,
+        ADD_IN_PROCESSOR,
+        ADD_IN_LISTENER;
+    }
+
+    enum RemoveKind {
+        REMOVE_IN_TASK,
+        REMOVE_IN_PROCESSOR,
+        REMOVE_IN_LISTENER,
+    }
+
+    enum CompileKind {
+        CALL {
+            void run(JavacTask t) {
+                if (!t.call()) throw new Error("compilation failed");
+            }
+        },
+        GENERATE {
+            void run(JavacTask t) throws IOException {
+                t.generate();
+            }
+        };
+        abstract void run(JavacTask t) throws IOException;
+    }
+
+    static class EventKindCounter extends EnumMap<TaskEvent.Kind, EventKindCounter.Count> {
+        static class Count {
+            int started;
+            int finished;
+
+            @Override
+            public String toString() {
+                return started + ":" + finished;
+            }
+        }
+
+        EventKindCounter() {
+            super(TaskEvent.Kind.class);
+        }
+
+        void inc(TaskEvent.Kind k, boolean started) {
+            Count c = get(k);
+            if (c == null)
+                put(k, c = new Count());
+
+            if (started)
+                c.started++;
+            else
+                c.finished++;
+        }
+    }
+
+    static class TestListener implements TaskListener {
+        EventKindCounter counter;
+
+        TestListener(EventKindCounter c) {
+            counter = c;
+        }
+
+        public void started(TaskEvent e) {
+            counter.inc(e.getKind(), true);
+        }
+
+        public void finished(TaskEvent e) {
+            counter.inc(e.getKind(), false);
+        }
+    }
+
+    static void addInListener(final JavacTask task, final TaskEvent.Kind kind, final TaskListener listener) {
+        task.addTaskListener(new TaskListener() {
+            public void started(TaskEvent e) {
+                if (e.getKind() == kind) {
+                    task.addTaskListener(listener);
+                    task.removeTaskListener(this);
+                }
+            }
+
+            public void finished(TaskEvent e) { }
+        });
+    }
+
+    static void removeInListener(final JavacTask task, final TaskEvent.Kind kind, final TaskListener listener) {
+        task.addTaskListener(new TaskListener() {
+            public void started(TaskEvent e) {
+                if (e.getKind() == kind) {
+                    task.removeTaskListener(listener);
+                    task.removeTaskListener(this);
+                }
+            }
+
+            public void finished(TaskEvent e) { }
+        });
+    }
+
+    @SupportedAnnotationTypes("*")
+    class TestProcessor extends AbstractProcessor {
+        AddKind ak;
+        RemoveKind rk;
+        TaskListener listener;
+
+        TestProcessor(AddKind ak, RemoveKind rk, TaskListener listener) {
+            this.ak = ak;
+            this.rk = rk;
+            this.listener = listener;
+        }
+
+        int round = 0;
+
+        @Override
+        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+//            System.err.println("TestProcessor.process " + roundEnv);
+            JavacTask task = JavacTask.instance(processingEnv);
+            if (++round == 1) {
+                switch (ak) {
+                    case ADD_IN_PROCESSOR:
+                        task.addTaskListener(listener);
+                        break;
+                    case ADD_IN_LISTENER:
+                        addInListener(task, TaskEvent.Kind.ANALYZE, listener);
+                        break;
+                }
+            } else if (roundEnv.processingOver()) {
+                switch (rk) {
+                    case REMOVE_IN_PROCESSOR:
+                        task.removeTaskListener(listener);
+                        break;
+                    case REMOVE_IN_LISTENER:
+                        removeInListener(task, TaskEvent.Kind.GENERATE, listener);
+                        break;
+                }
+            }
+            return true;
+        }
+    }
+
+    static class TestSource extends SimpleJavaFileObject {
+        public TestSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return "class Test { }";
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+        new TestSimpleAddRemove().run();
+    }
+
+    JavacTool tool = (JavacTool) ToolProvider.getSystemJavaCompiler();
+    StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
+
+    void run() throws Exception {
+        for (CompileKind ck: CompileKind.values()) {
+            for (AddKind ak: AddKind.values()) {
+                for (RemoveKind rk: RemoveKind.values()) {
+                    test(ck, ak, rk);
+                }
+            }
+        }
+        if (errors > 0)
+            throw new Exception(errors + " errors occurred");
+    }
+
+    void test(CompileKind ck, AddKind ak, RemoveKind rk) throws IOException {
+        System.err.println("Test: " + ck + " " + ak + " " + rk);
+
+        File tmpDir = new File(ck + "-" + ak + "-" + rk);
+        tmpDir.mkdirs();
+        fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(tmpDir));
+
+        List<String> options = new ArrayList<String>();
+        Iterable<? extends JavaFileObject> files = Arrays.asList(new TestSource());
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        JavacTask task = tool.getTask(pw, fm, null, options, null, files);
+
+        EventKindCounter ec = new EventKindCounter();
+        TaskListener listener = new TestListener(ec);
+        boolean needProcessor = false;
+
+        switch (ak) {
+            case SET_IN_TASK:
+                task.setTaskListener(listener);
+                break;
+            case ADD_IN_TASK:
+                task.addTaskListener(listener);
+                break;
+            case ADD_IN_PROCESSOR:
+            case ADD_IN_LISTENER:
+                needProcessor = true;
+        }
+
+        switch (rk) {
+            case REMOVE_IN_TASK:
+                task.removeTaskListener(listener);
+                break;
+            case REMOVE_IN_PROCESSOR:
+            case REMOVE_IN_LISTENER:
+                needProcessor = true;
+        }
+
+        if (needProcessor)
+            task.setProcessors(Arrays.asList(new TestProcessor(ak, rk, listener)));
+
+        ck.run(task);
+        System.err.println(ec);
+
+        check(ck, ak, rk, ec);
+
+        System.err.println();
+    }
+
+    void check(CompileKind ck, AddKind ak, RemoveKind rk, EventKindCounter ec) {
+        // All results should be independent of ck, so we can ignore that
+
+        // Quick way to compare expected values of ec, by comparing ec.toString()
+        String expect = ec.toString();
+        String found;
+
+        switch (ak) {
+            // Add/set in task should record all events until the listener is removed
+            case SET_IN_TASK:
+            case ADD_IN_TASK:
+                switch (rk) {
+                    case REMOVE_IN_TASK:
+                        // Remove will succeed, meaning no events will be recorded
+                        found = "{}";
+                        break;
+                    case REMOVE_IN_PROCESSOR:
+                        found = "{PARSE=1:1, ENTER=2:2, ANNOTATION_PROCESSING=1:0, ANNOTATION_PROCESSING_ROUND=2:1}";
+                        break;
+                    case REMOVE_IN_LISTENER:
+                        found = "{PARSE=1:1, ENTER=3:3, ANALYZE=1:1, GENERATE=1:0, ANNOTATION_PROCESSING=1:1, ANNOTATION_PROCESSING_ROUND=2:2}";
+                        break;
+                    default:
+                        throw new IllegalStateException();
+                }
+                break;
+
+            // "Add in processor" should skip initial PARSE/ENTER events
+            case ADD_IN_PROCESSOR:
+                switch (rk) {
+                    // Remove will fail (too early), so events to end will be recorded
+                    case REMOVE_IN_TASK:
+                        found = "{ENTER=2:2, ANALYZE=1:1, GENERATE=1:1, ANNOTATION_PROCESSING=0:1, ANNOTATION_PROCESSING_ROUND=1:2}";
+                        break;
+                    case REMOVE_IN_PROCESSOR:
+                        found = "{ENTER=1:1, ANNOTATION_PROCESSING_ROUND=1:1}";
+                        break;
+                    case REMOVE_IN_LISTENER:
+                        found = "{ENTER=2:2, ANALYZE=1:1, GENERATE=1:0, ANNOTATION_PROCESSING=0:1, ANNOTATION_PROCESSING_ROUND=1:2}";
+                        break;
+                    default:
+                        throw new IllegalStateException();
+                }
+                break;
+
+            // "Add in listener" will occur during "ANALYSE.started" event
+            case ADD_IN_LISTENER:
+                switch (rk) {
+                    // Remove will fail (too early, so events to end will be recorded
+                    case REMOVE_IN_TASK:
+                    case REMOVE_IN_PROCESSOR:
+                        found = "{ANALYZE=0:1, GENERATE=1:1}";
+                        break;
+                    // Remove will succeed during "GENERATE.finished" event
+                    case REMOVE_IN_LISTENER:
+                        found = "{ANALYZE=0:1, GENERATE=1:0}";
+                        break;
+                    default:
+                        throw new IllegalStateException();
+                }
+                break;
+            default:
+                throw new IllegalStateException();
+        }
+
+        if (!found.equals(expect)) {
+            System.err.println("Expected: " + expect);
+            System.err.println("   Found: " + found);
+            error("unexpected value found");
+        }
+    }
+
+    int errors;
+
+    void error(String message) {
+        System.err.println("Error: " + message);
+        errors++;
+    }
+}
--- a/langtools/test/tools/javac/processing/loader/testClose/TestClose.java	Tue Feb 28 10:25:53 2012 -0800
+++ b/langtools/test/tools/javac/processing/loader/testClose/TestClose.java	Tue Feb 28 10:33:49 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
 import com.sun.source.util.TaskEvent;
 import com.sun.source.util.TaskListener;
 import com.sun.tools.javac.api.ClientCodeWrapper.Trusted;
+import com.sun.tools.javac.api.BasicJavacTask;
 import com.sun.tools.javac.api.JavacTool;
 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
 import com.sun.tools.javac.util.Context;
@@ -89,10 +90,10 @@
         "                public void run() {\n" +
         "                    System.out.println(getClass().getName() + \": run()\");\n" +
         "                    try {\n" +
-        "                    cl.loadClass(\"Callback\")\n" +
-        "                        .asSubclass(Runnable.class)\n" +
-        "                        .newInstance()\n" +
-        "                        .run();\n" +
+        "                        cl.loadClass(\"Callback\")\n" +
+        "                            .asSubclass(Runnable.class)\n" +
+        "                            .newInstance()\n" +
+        "                            .run();\n" +
         "                    } catch (ReflectiveOperationException e) {\n" +
         "                        throw new Error(e);\n" +
         "                    }\n" +
@@ -184,25 +185,24 @@
             throw new AssertionError();
     }
 
-
     public static void add(ProcessingEnvironment env, Runnable r) {
         try {
-            Context c = ((JavacProcessingEnvironment) env).getContext();
-            Object o = c.get(TaskListener.class);
+            JavacTask task = JavacTask.instance(env);
+            TaskListener l = ((BasicJavacTask) task).getTaskListeners().iterator().next();
             // The TaskListener is an instanceof TestClose, but when using the
             // default class loaders. the taskListener uses a different
             // instance of Class<TestClose> than the anno processor.
             // If you try to evaluate
-            //      TestClose tc = (TestClose) (o).
+            //      TestClose tc = (TestClose) (l).
             // you get the following somewhat confusing error:
             //   java.lang.ClassCastException: TestClose cannot be cast to TestClose
             // The workaround is to access the fields of TestClose with reflection.
-            Field f = o.getClass().getField("runnables");
+            Field f = l.getClass().getField("runnables");
             @SuppressWarnings("unchecked")
-            List<Runnable> runnables = (List<Runnable>) f.get(o);
+            List<Runnable> runnables = (List<Runnable>) f.get(l);
             runnables.add(r);
         } catch (Throwable t) {
-            System.err.println(t);
+            t.printStackTrace();
         }
     }