changeset 3338:3571fc58f820

8049819: specify annotation processors in modules
author vromero
date Fri, 02 Oct 2015 11:40:25 -0700
parents b66a1d691afa
children b687fdf16cb4
files src/java.compiler/share/classes/javax/tools/JavaFileManager.java src/java.compiler/share/classes/javax/tools/StandardLocation.java src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties src/jdk.compiler/share/classes/com/sun/tools/javac/util/ModuleWrappers.java src/jdk.compiler/share/classes/module-info.java test/tools/javac/api/TestClientCodeWrapper.java test/tools/javac/modules/AnnotationProcessorsInModulesTest.java test/tools/javac/modules/PluginsInModulesTest.java test/tools/javac/plugin/showtype/Test.java test/tools/lib/ToolBox.java
diffstat 18 files changed, 963 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.compiler/share/classes/javax/tools/JavaFileManager.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/java.compiler/share/classes/javax/tools/JavaFileManager.java	Fri Oct 02 11:40:25 2015 -0700
@@ -29,7 +29,9 @@
 import java.io.Flushable;
 import java.io.IOException;
 import java.util.Iterator;
+import java.util.ServiceLoader;
 import java.util.Set;
+
 import static javax.tools.JavaFileObject.Kind;
 
 /**
@@ -124,6 +126,16 @@
          * @return true if this is an output location, false otherwise
          */
         boolean isOutputLocation();
+
+        /**
+         * Indicates if this location is expected to contain modules,
+         * as compared to a location which contains packages and classes.
+         *
+         * @return true if this location is expected to contain modules
+         */
+        default boolean isModuleLocation() {
+            return false;
+        }
     }
 
     /**
@@ -417,6 +429,16 @@
     }
 
     /**
+     * Get a service loader for a specific service class from a given location.
+     * @param location the location
+     * @param service  the service class
+     * @return a service loader for the given service class
+     */
+    default <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws  IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
      * Infer the name of the module from its location, as returned by
      * getModuleLocation or listModuleLocations.
      * @param location
--- a/src/java.compiler/share/classes/javax/tools/StandardLocation.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/java.compiler/share/classes/javax/tools/StandardLocation.java	Fri Oct 02 11:40:25 2015 -0700
@@ -63,6 +63,11 @@
     ANNOTATION_PROCESSOR_PATH,
 
     /**
+     * Location to search for modules containing annotation processors.
+     */
+    ANNOTATION_PROCESSOR_MODULE_PATH,
+
+    /**
      * Location to search for platform classes.  Sometimes called
      * the boot class path.
      */
@@ -137,4 +142,17 @@
                 return false;
         }
     }
+
+    @Override
+    public boolean isModuleLocation() {
+        switch (this) {
+            case ANNOTATION_PROCESSOR_MODULE_PATH:
+            case UPGRADE_MODULE_PATH:
+            case SYSTEM_MODULE_PATH:
+            case MODULE_PATH:
+                return true;
+            default:
+                return false;
+        }
+    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java	Fri Oct 02 11:40:25 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, 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
@@ -195,8 +195,7 @@
 
         Set<List<String>> pluginsToCall = new LinkedHashSet<>(pluginOpts);
         JavacProcessingEnvironment pEnv = JavacProcessingEnvironment.instance(context);
-        ClassLoader cl = pEnv.getProcessorClassLoader();
-        ServiceLoader<Plugin> sl = ServiceLoader.load(Plugin.class, cl);
+        ServiceLoader<Plugin> sl = pEnv.getServiceLoader(Plugin.class);
         for (Plugin plugin : sl) {
             for (List<String> p : pluginsToCall) {
                 if (plugin.getName().equals(p.head)) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Fri Oct 02 11:40:25 2015 -0700
@@ -50,6 +50,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
+import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -68,6 +69,11 @@
 import com.sun.tools.javac.util.DefinedBy.Api;
 import com.sun.tools.javac.util.List;
 import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.ModuleWrappers.Configuration;
+import com.sun.tools.javac.util.ModuleWrappers.Layer;
+import com.sun.tools.javac.util.ModuleWrappers.ModuleClassLoader;
+import com.sun.tools.javac.util.ModuleWrappers.ModuleFinder;
+import com.sun.tools.javac.util.ModuleWrappers.ServiceLoaderHelper;
 
 import static javax.tools.StandardLocation.*;
 
@@ -940,7 +946,23 @@
         nullCheck(location);
         nullCheck(moduleName);
         return locations.getModuleLocation(location, moduleName);
+    }
 
+    @Override @DefinedBy(Api.COMPILER)
+    public <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws IOException {
+        nullCheck(location);
+        nullCheck(service);
+        if (location.isModuleLocation()) {
+            Collection<Path> paths = locations.getLocation(location);
+            ModuleFinder finder = ModuleFinder.of(paths.toArray(new Path[paths.size()]));
+            Configuration cf = Configuration.resolve(ModuleFinder.empty(), Layer.boot(), finder);
+            cf = cf.bind();
+            ModuleClassLoader cl = new ModuleClassLoader(cf);
+            Layer layer = Layer.create(cf, cl);
+            return ServiceLoaderHelper.load(layer, service);
+        } else {
+            return ServiceLoader.load(service, getClassLoader(location));
+        }
     }
 
     @Override @DefinedBy(Api.COMPILER)
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Fri Oct 02 11:40:25 2015 -0700
@@ -1386,6 +1386,7 @@
             new ClassPathLocationHandler(),
             new SimpleLocationHandler(StandardLocation.SOURCE_PATH, Option.SOURCEPATH),
             new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, Option.PROCESSORPATH),
+            new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, Option.PROCESSORMODULEPATH),
             new OutputLocationHandler(StandardLocation.CLASS_OUTPUT, Option.D),
             new OutputLocationHandler(StandardLocation.SOURCE_OUTPUT, Option.S),
             new OutputLocationHandler(StandardLocation.NATIVE_HEADER_OUTPUT, Option.H),
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java	Fri Oct 02 11:40:25 2015 -0700
@@ -520,6 +520,11 @@
             }
         }
 
+        if (fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH) &&
+            fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH)) {
+            log.error("processorpath.no.procesormodulepath");
+        }
+
         if (obsoleteOptionFound)
             log.warning(LintCategory.OPTIONS, "option.obsolete.suppression");
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Fri Oct 02 11:40:25 2015 -0700
@@ -1281,6 +1281,7 @@
         return
             options.isSet(PROCESSOR) ||
             options.isSet(PROCESSORPATH) ||
+            options.isSet(PROCESSORMODULEPATH) ||
             options.isSet(PROC, "only") ||
             options.isSet(XPRINT);
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java	Fri Oct 02 11:40:25 2015 -0700
@@ -252,6 +252,8 @@
 
     PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER),
 
+    PROCESSORMODULEPATH("-processormodulepath", "opt.arg.path", "opt.processormodulepath", STANDARD, FILEMANAGER),
+
     PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC),
 
     D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Fri Oct 02 11:40:25 2015 -0700
@@ -27,6 +27,7 @@
 
 import java.io.Closeable;
 import java.io.File;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.reflect.Method;
@@ -159,7 +160,10 @@
     Source source;
 
     private ClassLoader processorClassLoader;
-    private SecurityException processorClassLoaderException;
+    private ServiceLoader<Processor> serviceLoader;
+    private SecurityException processorLoaderException;
+
+    private final JavaFileManager fileManager;
 
     /**
      * JavacMessages object used for localization
@@ -202,6 +206,7 @@
         fatalErrors = options.isSet("fatalEnterError");
         showResolveErrors = options.isSet("showResolveErrors");
         werror = options.isSet(WERROR);
+        fileManager = context.get(JavaFileManager.class);
         platformAnnotations = initPlatformAnnotations();
 
         // Initialize services before any processors are initialized
@@ -222,7 +227,7 @@
         initialCompleter = ClassFinder.instance(context).getCompleter();
         chk = Check.instance(context);
         moduleHelper = ModuleHelper.instance(context);
-        initProcessorClassLoader();
+        initProcessorLoader();
 
         defaultModule = source.allowModules() && options.isUnset("noModules")
                 ? symtab.unnamedModule : symtab.noModule;
@@ -245,21 +250,28 @@
         return Collections.unmodifiableSet(platformAnnotations);
     }
 
-    private void initProcessorClassLoader() {
-        JavaFileManager fileManager = context.get(JavaFileManager.class);
+    private void initProcessorLoader() {
         try {
-            // If processorpath is not explicitly set, use the classpath.
-            processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
-                ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
-                : fileManager.getClassLoader(CLASS_PATH);
+            if (fileManager.hasLocation(ANNOTATION_PROCESSOR_MODULE_PATH)) {
+                try {
+                    serviceLoader = fileManager.getServiceLoader(ANNOTATION_PROCESSOR_MODULE_PATH, Processor.class);
+                } catch (IOException e) {
+                    throw new Abort(e);
+                }
+            } else {
+                // If processorpath is not explicitly set, use the classpath.
+                processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
+                    ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
+                    : fileManager.getClassLoader(CLASS_PATH);
 
-            moduleHelper.addExports(processorClassLoader);
+                moduleHelper.addExports(processorClassLoader);
 
-            if (processorClassLoader != null && processorClassLoader instanceof Closeable) {
-                compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader);
+                if (processorClassLoader != null && processorClassLoader instanceof Closeable) {
+                    compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader);
+                }
             }
         } catch (SecurityException e) {
-            processorClassLoaderException = e;
+            processorLoaderException = e;
         }
     }
 
@@ -279,14 +291,18 @@
         } else if (processors != null) {
             processorIterator = processors.iterator();
         } else {
-            String processorNames = options.get(PROCESSOR);
-            if (processorClassLoaderException == null) {
+            if (processorLoaderException == null) {
                 /*
                  * If the "-processor" option is used, search the appropriate
                  * path for the named class.  Otherwise, use a service
                  * provider mechanism to create the processor iterator.
                  */
-                if (processorNames != null) {
+                String processorNames = options.get(PROCESSOR);
+                if (fileManager.hasLocation(ANNOTATION_PROCESSOR_MODULE_PATH)) {
+                    processorIterator = (processorNames == null) ?
+                            new ServiceIterator(serviceLoader, log) :
+                            new NameServiceIterator(serviceLoader, log, processorNames);
+                } else if (processorNames != null) {
                     processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log);
                 } else {
                     processorIterator = new ServiceIterator(processorClassLoader, log);
@@ -299,7 +315,7 @@
                  * in service configuration file.) Otherwise, we cannot continue.
                  */
                 processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader",
-                        processorClassLoaderException);
+                        processorLoaderException);
             }
         }
         PlatformDescription platformProvider = context.get(PlatformDescription.class);
@@ -317,6 +333,18 @@
         discoveredProcs = new DiscoveredProcessors(compoundIterator);
     }
 
+    public <S> ServiceLoader<S> getServiceLoader(Class<S> service) {
+        if (fileManager.hasLocation(ANNOTATION_PROCESSOR_MODULE_PATH)) {
+            try {
+                return fileManager.getServiceLoader(ANNOTATION_PROCESSOR_MODULE_PATH, service);
+            } catch (IOException e) {
+                throw new Abort(e);
+            }
+        } else {
+            return ServiceLoader.load(service, getProcessorClassLoader());
+        }
+    }
+
     /**
      * Returns an empty processor iterator if no processors are on the
      * relevant path, otherwise if processors are present, logs an
@@ -329,8 +357,6 @@
      * @param e   If non-null, pass this exception to Abort
      */
     private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) {
-        JavaFileManager fileManager = context.get(JavaFileManager.class);
-
         if (fileManager instanceof JavacFileManager) {
             StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager;
             Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
@@ -368,9 +394,9 @@
      * needed but unavailable.
      */
     private class ServiceIterator implements Iterator<Processor> {
-        private Iterator<Processor> iterator;
-        private Log log;
-        private ServiceLoader<Processor> loader;
+        Iterator<Processor> iterator;
+        Log log;
+        ServiceLoader<Processor> loader;
 
         ServiceIterator(ClassLoader classLoader, Log log) {
             this.log = log;
@@ -388,9 +414,16 @@
             }
         }
 
+        ServiceIterator(ServiceLoader<Processor> loader, Log log) {
+            this.log = log;
+            this.loader = loader;
+            this.iterator = loader.iterator();
+        }
+
+        @Override
         public boolean hasNext() {
             try {
-                return iterator.hasNext();
+                return internalHasNext();
             } catch(ServiceConfigurationError sce) {
                 log.error("proc.bad.config.file", sce.getLocalizedMessage());
                 throw new Abort(sce);
@@ -399,9 +432,14 @@
             }
         }
 
+        boolean internalHasNext() {
+            return iterator.hasNext();
+        }
+
+        @Override
         public Processor next() {
             try {
-                return iterator.next();
+                return internalNext();
             } catch (ServiceConfigurationError sce) {
                 log.error("proc.bad.config.file", sce.getLocalizedMessage());
                 throw new Abort(sce);
@@ -410,6 +448,11 @@
             }
         }
 
+        Processor internalNext() {
+            return iterator.next();
+        }
+
+        @Override
         public void remove() {
             throw new UnsupportedOperationException();
         }
@@ -425,6 +468,58 @@
         }
     }
 
+    private class NameServiceIterator extends ServiceIterator {
+        private Map<String, Processor> namedProcessorsMap = new HashMap<>();;
+        private Iterator<String> processorNames = null;
+        private Processor nextProc = null;
+
+        public NameServiceIterator(ServiceLoader<Processor> loader, Log log, String theNames) {
+            super(loader, log);
+            this.processorNames = Arrays.asList(theNames.split(",")).iterator();
+        }
+
+        @Override
+        boolean internalHasNext() {
+            if (nextProc != null) {
+                return true;
+            }
+            if (!processorNames.hasNext()) {
+                namedProcessorsMap = null;
+                return false;
+            }
+            String processorName = processorNames.next();
+            Processor theProcessor = namedProcessorsMap.get(processorName);
+            if (theProcessor != null) {
+                namedProcessorsMap.remove(processorName);
+                nextProc = theProcessor;
+                return true;
+            } else {
+                while (iterator.hasNext()) {
+                    theProcessor = iterator.next();
+                    String name = theProcessor.getClass().getName();
+                    if (name.equals(processorName)) {
+                        nextProc = theProcessor;
+                        return true;
+                    } else {
+                        namedProcessorsMap.put(name, theProcessor);
+                    }
+                }
+                log.error("proc.processor.not.found", processorName);
+                return false;
+            }
+        }
+
+        @Override
+        Processor internalNext() {
+            if (hasNext()) {
+                Processor p = nextProc;
+                nextProc = null;
+                return p;
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+    }
 
     private static class NameProcessIterator implements Iterator<Processor> {
         Processor nextProc = null;
@@ -640,7 +735,7 @@
     // TODO: These two classes can probably be rewritten better...
     /**
      * This class holds information about the processors that have
-     * been discoverd so far as well as the means to discover more, if
+     * been discovered so far as well as the means to discover more, if
      * necessary.  A single iterator should be used per round of
      * annotation processing.  The iterator first visits already
      * discovered processors then fails over to the service provider
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Oct 02 11:40:25 2015 -0700
@@ -2725,6 +2725,9 @@
 compiler.err.xmodule.no.module.sourcepath=\
     illegal combination of -Xmodule and -modulesourcepath
 
+compiler.err.processorpath.no.procesormodulepath=\
+    illegal combination of -processorpath and -procesormodulepath
+
 # 0: symbol
 compiler.warn.package.in.other.module.1=\
     package exists in another module: {0}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties	Fri Oct 02 11:40:25 2015 -0700
@@ -65,6 +65,8 @@
     Override location of installed extensions
 javac.opt.processorpath=\
     Specify where to find annotation processors
+javac.opt.processormodulepath=\
+    Specify a module path where to find annotation processors
 javac.opt.processor=\
     Names of the annotation processors to run; bypasses default discovery process
 javac.opt.parameters=\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/ModuleWrappers.java	Fri Oct 02 11:40:25 2015 -0700
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2015, 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.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.nio.file.Path;
+import java.security.SecureClassLoader;
+import java.util.ServiceLoader;
+
+/** This class provides wrappers for classes and methods that are new in JDK 9, and which are not
+ *  available on older versions of the platform on which javac may be compiled and run.
+ *  In future releases, when javac is always compiled on JDK 9 or later, the use of these wrappers
+ *  can be replaced by use of the real underlying classes.
+ */
+public class ModuleWrappers {
+    public static final class ServiceLoaderHelper {
+        @SuppressWarnings("unchecked")
+        public static <S> ServiceLoader<S> load(Layer layer, Class<S> service) {
+            try {
+                Class<?> layerClass = LayerHelper.getLayerClass();
+                Method loadMethod = ServiceLoader.class
+                        .getDeclaredMethod("load", layerClass, Class.class);
+                Object result = loadMethod.invoke(ServiceLoader.class, layer.theRealLayer, service);
+                return (ServiceLoader<S>)result;
+            } catch (NoSuchMethodException |
+                    SecurityException |
+                    IllegalArgumentException |
+                    IllegalAccessException |
+                    InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+    }
+
+    public static final class ModuleClassLoader extends SecureClassLoader {
+        Object theRealModuleClassLoader;
+
+        private ModuleClassLoader(Object moduleClassLoader) {
+            this.theRealModuleClassLoader = moduleClassLoader;
+        }
+
+        public ModuleClassLoader(Configuration configuration) {
+            try {
+                theRealModuleClassLoader = ModuleClassLoaderHelper.getConfigurationCtor().newInstance(configuration.theRealConfiguration);
+            } catch (InstantiationException |
+                    IllegalAccessException |
+                    IllegalArgumentException |
+                    InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+    }
+
+    private static class ModuleClassLoaderHelper {
+        static Constructor<?> configurationCtor = null;
+        static Class<?> moduleClassLoaderClass;
+
+        static Constructor<?> getConfigurationCtor() {
+            if (moduleClassLoaderClass == null) {
+                try {
+                    Class<?> configurationClass = ConfigurationHelper.getConfigurationClass();
+                    configurationCtor = getModuleClassLoaderClass().getDeclaredConstructor(new Class<?>[] { configurationClass });
+                } catch (NoSuchMethodException | SecurityException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return configurationCtor;
+        }
+
+        static Class<?> getModuleClassLoaderClass() {
+            if (moduleClassLoaderClass == null) {
+                try {
+                    moduleClassLoaderClass = Class.forName("java.lang.ModuleClassLoader", false, ClassLoader.getSystemClassLoader());
+                } catch (ClassNotFoundException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return moduleClassLoaderClass;
+        }
+    }
+
+    public static class ModuleFinder {
+        Object theRealModuleFinder;
+
+        private ModuleFinder(Object moduleFinder) {
+            this.theRealModuleFinder = moduleFinder;
+        }
+
+        public static ModuleFinder of(Path... dirs) {
+            try {
+                Object result = ModuleFinderHelper.getOfMethod()
+                        .invoke(ModuleFinderHelper.moduleFinderInterface, (Object)dirs);
+                ModuleFinder mFinder = new ModuleFinder(result);
+                return mFinder;
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+
+        public static ModuleFinder empty() {
+            try {
+                Object result = ModuleFinderHelper.getEmptyMethod()
+                        .invoke(ModuleFinderHelper.moduleFinderInterface);
+                ModuleFinder mFinder = new ModuleFinder(result);
+                return mFinder;
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+    }
+
+    private static class ModuleFinderHelper {
+        static Method ofMethod = null;
+        static Method emptyMethod = null;
+        static Class<?> moduleFinderInterface;
+
+        static Method getOfMethod() {
+            if (ModuleFinderHelper.ofMethod == null) {
+                try {
+                    getModuleFinderInterface();
+                    ofMethod = moduleFinderInterface.getDeclaredMethod("of", Path[].class);
+                } catch (NoSuchMethodException | SecurityException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return ofMethod;
+        }
+
+        static Method getEmptyMethod() {
+            if (emptyMethod == null) {
+                try {
+                    getModuleFinderInterface();
+                    emptyMethod = moduleFinderInterface.getDeclaredMethod("empty");
+                } catch (NoSuchMethodException | SecurityException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return emptyMethod;
+        }
+
+        static Class<?> getModuleFinderInterface() {
+            if (moduleFinderInterface == null) {
+                try {
+                    moduleFinderInterface = Class.forName("java.lang.module.ModuleFinder", false, ClassLoader.getSystemClassLoader());
+                } catch (ClassNotFoundException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return moduleFinderInterface;
+        }
+    }
+
+    public static final class Configuration {
+        Object theRealConfiguration;
+
+        private Configuration(Object configuration) {
+            this.theRealConfiguration = configuration;
+        }
+
+        public static Configuration resolve(
+                ModuleFinder beforeFinder,
+                Layer parent,
+                ModuleFinder afterFinder,
+                String... roots) {
+            try {
+                Object result = ConfigurationHelper.getResolveMethod()
+                        .invoke(ConfigurationHelper.getConfigurationClass(),
+                                    beforeFinder.theRealModuleFinder,
+                                    parent.theRealLayer,
+                                    afterFinder.theRealModuleFinder,
+                                    roots
+                                );
+                Configuration configuration = new Configuration(result);
+                return configuration;
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+
+        public Configuration bind() {
+            try {
+                Object result = ConfigurationHelper.getBindMethod().invoke(theRealConfiguration);
+                Configuration configuration = new Configuration(result);
+                return configuration;
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+    }
+
+    private static class ConfigurationHelper {
+        static Method resolveMethod = null;
+        static Method bindMethod = null;
+        static Class<?> configurationClass;
+
+        static Method getResolveMethod() {
+            if (ConfigurationHelper.resolveMethod == null) {
+                try {
+                    getConfigurationClass();
+                    Class<?> moduleFinderInterface = ModuleFinderHelper.getModuleFinderInterface();
+                    Class<?> layerClass = LayerHelper.getLayerClass();
+                    resolveMethod = configurationClass.getDeclaredMethod("resolve",
+                                moduleFinderInterface,
+                                layerClass,
+                                moduleFinderInterface,
+                                String[].class
+                    );
+                } catch (NoSuchMethodException | SecurityException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return resolveMethod;
+        }
+
+        static Method getBindMethod() {
+            if (bindMethod == null) {
+                try {
+                    bindMethod = getConfigurationClass().getDeclaredMethod("bind");
+                } catch (NoSuchMethodException | SecurityException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return bindMethod;
+        }
+
+        static Class<?> getConfigurationClass() {
+            if (configurationClass == null) {
+                try {
+                    configurationClass = Class.forName("java.lang.module.Configuration", false, ClassLoader.getSystemClassLoader());
+                } catch (ClassNotFoundException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return configurationClass;
+        }
+    }
+
+    public static final class Layer {
+        Object theRealLayer;
+
+        private Layer(Object layer) {
+            this.theRealLayer = layer;
+        }
+
+        public static Layer boot() {
+            try {
+                Object result = LayerHelper.getBootMethod().invoke(LayerHelper.getLayerClass());
+                Layer layer = new Layer(result);
+                return layer;
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+
+        public static Layer create(Configuration configuration, ModuleClassLoader modClassLoader) {
+            try {
+                Class<?> classLoaderFinderInterface = LayerHelper.getClassLoaderFinderInterface();
+                LayerHelper.ClassLoaderFinderInvocationHandler handler =
+                        new LayerHelper.ClassLoaderFinderInvocationHandler(modClassLoader);
+                Object proxy = Proxy.newProxyInstance(
+                                            ClassLoader.getSystemClassLoader(),
+                                            new Class<?>[] { classLoaderFinderInterface },
+                                            handler);
+                Object result = LayerHelper.getCreateMethod()
+                        .invoke(LayerHelper.getLayerClass(), configuration.theRealConfiguration, proxy);
+                Layer layer = new Layer(result);
+                return layer;
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+                throw new Abort(ex);
+            }
+        }
+    }
+
+    private static class LayerHelper {
+        static Class<?> layerClass;
+        static Class<?> classLoaderFinderInterface;
+        static Method bootMethod = null;
+        static Method createMethod = null;
+
+        static Class<?> getLayerClass() {
+            if (layerClass == null) {
+                try {
+                    layerClass = Class.forName("java.lang.reflect.Layer", false, ClassLoader.getSystemClassLoader());
+                } catch (ClassNotFoundException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return layerClass;
+        }
+
+        static class ClassLoaderFinderInvocationHandler implements InvocationHandler {
+            ModuleClassLoader moduleClassLoader;
+
+            public ClassLoaderFinderInvocationHandler(ModuleClassLoader moduleClassLoader) {
+                this.moduleClassLoader = moduleClassLoader;
+            }
+
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                return moduleClassLoader.theRealModuleClassLoader;
+            }
+        }
+
+        static Class<?> getClassLoaderFinderInterface() {
+            if (classLoaderFinderInterface == null) {
+                try {
+                    classLoaderFinderInterface = Class.forName("java.lang.reflect.Layer$ClassLoaderFinder", false, ClassLoader.getSystemClassLoader());
+                } catch (ClassNotFoundException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return classLoaderFinderInterface;
+        }
+
+        static Method getBootMethod() {
+            if (bootMethod == null) {
+                try {
+                    bootMethod = getLayerClass().getDeclaredMethod("boot");
+                } catch (NoSuchMethodException | SecurityException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return bootMethod;
+        }
+
+        static Method getCreateMethod() {
+            if (createMethod == null) {
+                try {
+                    createMethod = getLayerClass().getDeclaredMethod("create",
+                                ConfigurationHelper.getConfigurationClass(),
+                                getClassLoaderFinderInterface()
+                    );
+                } catch (NoSuchMethodException | SecurityException ex) {
+                    throw new Abort(ex);
+                }
+            }
+            return createMethod;
+        }
+    }
+}
--- a/src/jdk.compiler/share/classes/module-info.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/src/jdk.compiler/share/classes/module-info.java	Fri Oct 02 11:40:25 2015 -0700
@@ -59,7 +59,9 @@
     uses javax.annotation.processing.Processor;
     uses com.sun.source.util.Plugin;
     uses com.sun.tools.javac.platform.PlatformProvider;
-    provides com.sun.tools.javac.platform.PlatformProvider with com.sun.tools.javac.platform.JDKPlatformProvider;
+
+    provides com.sun.tools.javac.platform.PlatformProvider
+        with com.sun.tools.javac.platform.JDKPlatformProvider;
 
     provides javax.tools.JavaCompiler
         with com.sun.tools.javac.api.JavacTool;
--- a/test/tools/javac/api/TestClientCodeWrapper.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/test/tools/javac/api/TestClientCodeWrapper.java	Fri Oct 02 11:40:25 2015 -0700
@@ -39,9 +39,10 @@
 import javax.lang.model.*;
 import javax.lang.model.element.*;
 import javax.tools.*;
+import javax.tools.JavaFileObject.Kind;
+
 import com.sun.source.util.*;
 import com.sun.tools.javac.api.*;
-import javax.tools.JavaFileObject.Kind;
 
 public class TestClientCodeWrapper extends JavacTestingAbstractProcessor {
     public static void main(String... args) throws Exception {
@@ -60,7 +61,8 @@
         try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
             defaultFileManager = fm;
 
-            for (Method m: getMethodsExcept(JavaFileManager.class, "close", "getJavaFileForInput", "getModuleLocation")) {
+            for (Method m: getMethodsExcept(JavaFileManager.class,
+                        "close", "getJavaFileForInput", "getModuleLocation", "getServiceLoader")) {
                 test(m);
             }
 
@@ -321,6 +323,12 @@
         }
 
         @Override
+        public <S> ServiceLoader getServiceLoader(Location location, Class<S> service) throws IOException {
+            throwUserExceptionIfNeeded(fileManagerMethod, "getServiceLoader");
+            return super.getServiceLoader(location, service);
+        }
+
+        @Override
         public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
             throwUserExceptionIfNeeded(fileManagerMethod, "list");
             return wrap(super.list(location, packageName, kinds, recurse));
@@ -416,7 +424,6 @@
             return super.listModuleLocations(location);
         }
 
-
         public FileObject wrap(FileObject fo) {
             if (fileObjectMethod == null || fo == null)
                 return fo;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/modules/AnnotationProcessorsInModulesTest.java	Fri Oct 02 11:40:25 2015 -0700
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2015, 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
+ * @summary Verify that annotation processors inside modules works
+ * @library /tools/lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ * @build ToolBox ModuleTestBase
+ * @run main AnnotationProcessorsInModulesTest
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import com.sun.source.util.JavacTask;
+
+public class AnnotationProcessorsInModulesTest extends ModuleTestBase {
+
+    public static void main(String... args) throws Exception {
+        new AnnotationProcessorsInModulesTest().runTests();
+    }
+
+    private static final String annotationProcessorModule1 =
+            "module anno_proc1 {\n" +
+            "    requires java.compiler;\n" +
+            "\n" +
+            "    provides javax.annotation.processing.Processor\n" +
+            "      with mypkg1.MyProcessor1;\n" +
+            "}";
+
+    private static final String annotationProcessorModule2 =
+            "module anno_proc2 {\n" +
+            "    requires java.compiler;\n" +
+            "\n" +
+            "    provides javax.annotation.processing.Processor\n" +
+            "      with mypkg2.MyProcessor2;\n" +
+            "}";
+
+    private static final String annotationProcessor1 =
+            "package mypkg1;\n" +
+            "\n" +
+            "import javax.annotation.processing.AbstractProcessor;\n" +
+            "import javax.annotation.processing.RoundEnvironment;\n" +
+            "import javax.annotation.processing.SupportedAnnotationTypes;\n" +
+            "import javax.lang.model.SourceVersion;\n" +
+            "import javax.lang.model.element.*;\n" +
+            "\n" +
+            "import java.util.*;\n" +
+            "\n" +
+            "@SupportedAnnotationTypes(\"*\")\n" +
+            "public final class MyProcessor1 extends AbstractProcessor {\n" +
+            "\n" +
+            "    @Override\n" +
+            "    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" +
+            "        return false;\n" +
+            "    }\n" +
+            "\n" +
+            "    @Override\n" +
+            "    public SourceVersion getSupportedSourceVersion() {\n" +
+            "        System.out.println(\"the annotation processor 1 is working!\");\n" +
+            "        return SourceVersion.latest();\n" +
+            "    }\n" +
+            "}";
+
+    private static final String annotationProcessor2 =
+            "package mypkg2;\n" +
+            "\n" +
+            "import javax.annotation.processing.AbstractProcessor;\n" +
+            "import javax.annotation.processing.RoundEnvironment;\n" +
+            "import javax.annotation.processing.SupportedAnnotationTypes;\n" +
+            "import javax.lang.model.SourceVersion;\n" +
+            "import javax.lang.model.element.*;\n" +
+            "\n" +
+            "import java.util.*;\n" +
+            "\n" +
+            "@SupportedAnnotationTypes(\"*\")\n" +
+            "public final class MyProcessor2 extends AbstractProcessor {\n" +
+            "\n" +
+            "    @Override\n" +
+            "    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" +
+            "        return false;\n" +
+            "    }\n" +
+            "\n" +
+            "    @Override\n" +
+            "    public SourceVersion getSupportedSourceVersion() {\n" +
+            "        System.out.println(\"the annotation processor 2 is working!\");\n" +
+            "        return SourceVersion.latest();\n" +
+            "    }\n" +
+            "}";
+
+    private static final String testClass = "class Test{}";
+
+    void initialization(Path base) throws Exception {
+        moduleSrc = base.resolve("anno_proc_src");
+        Path anno_proc1 = moduleSrc.resolve("anno_proc1");
+        Path anno_proc2 = moduleSrc.resolve("anno_proc2");
+
+        processorCompiledModules = base.resolve("mods");
+
+        Files.createDirectories(processorCompiledModules);
+
+        tb.writeJavaFiles(
+                anno_proc1,
+                annotationProcessorModule1,
+                annotationProcessor1);
+
+        tb.writeJavaFiles(
+                anno_proc2,
+                annotationProcessorModule2,
+                annotationProcessor2);
+
+        String log = tb.new JavacTask()
+                .options("-modulesourcepath", moduleSrc.toString())
+                .outdir(processorCompiledModules)
+                .files(findJavaFiles(moduleSrc))
+                .run()
+                .writeAll()
+                .getOutput(ToolBox.OutputKind.DIRECT);
+
+        if (!log.isEmpty()) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+
+        classes = base.resolve("classes");
+        Files.createDirectories(classes);
+    }
+
+    Path processorCompiledModules;
+    Path moduleSrc;
+    Path classes;
+
+    @Test
+    void testUseOnlyOneProcessor(Path base) throws Exception {
+        initialization(base);
+        String log = tb.new JavacTask()
+                .options("-processormodulepath", processorCompiledModules.toString(),
+                        "-processor", "mypkg2.MyProcessor2")
+                .outdir(classes)
+                .sources(testClass)
+                .run()
+                .writeAll()
+                .getOutput(ToolBox.OutputKind.STDOUT);
+        if (!log.trim().equals("the annotation processor 2 is working!")) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+
+    @Test
+    void testAnnotationProcessorExecutionOrder(Path base) throws Exception {
+        initialization(base);
+        String log = tb.new JavacTask()
+                .options("-processormodulepath", processorCompiledModules.toString(),
+                        "-processor", "mypkg1.MyProcessor1,mypkg2.MyProcessor2")
+                .outdir(classes)
+                .sources(testClass)
+                .run()
+                .writeAll()
+                .getOutput(ToolBox.OutputKind.STDOUT);
+        if (!log.trim().equals("the annotation processor 1 is working!\n" +
+                               "the annotation processor 2 is working!")) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+
+        log = tb.new JavacTask()
+                .options("-processormodulepath", processorCompiledModules.toString(),
+                        "-processor", "mypkg2.MyProcessor2,mypkg1.MyProcessor1")
+                .outdir(classes)
+                .sources(testClass)
+                .run()
+                .writeAll()
+                .getOutput(ToolBox.OutputKind.STDOUT);
+        if (!log.trim().equals("the annotation processor 2 is working!\n" +
+                               "the annotation processor 1 is working!")) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+
+    @Test
+    void testErrorOutputIfOneProcessorNameIsIncorrect(Path base) throws Exception {
+        initialization(base);
+        String log = tb.new JavacTask()
+                .options("-XDrawDiagnostics", "-processormodulepath", processorCompiledModules.toString(),
+                         "-processor", "mypkg2.MyProcessor2,noPackage.noProcessor,mypkg1.MyProcessor1")
+                .outdir(classes)
+                .sources(testClass)
+                .run(ToolBox.Expect.FAIL)
+                .writeAll()
+                .getOutputLines(ToolBox.OutputKind.STDOUT, ToolBox.OutputKind.DIRECT).toString();
+        if (!log.trim().equals("[the annotation processor 2 is working!, - compiler.err.proc.processor.not.found: noPackage.noProcessor, 1 error]")) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+
+    @Test
+    void testOptionsExclusion(Path base) throws Exception {
+        initialization(base);
+        String log = tb.new JavacTask()
+                .options("-XDrawDiagnostics", "-processormodulepath", processorCompiledModules.toString(),
+                        "-processorpath", processorCompiledModules.toString())
+                .outdir(classes)
+                .sources(testClass)
+                .run(ToolBox.Expect.FAIL)
+                .writeAll()
+                .getOutput(ToolBox.OutputKind.DIRECT);
+        if (!log.trim().equals("- compiler.err.processorpath.no.procesormodulepath\n" +
+                               "1 error")) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/modules/PluginsInModulesTest.java	Fri Oct 02 11:40:25 2015 -0700
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, 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
+ * @summary Verify that plugins inside modules works
+ * @library /tools/lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ * @build ToolBox ModuleTestBase
+ * @run main PluginsInModulesTest
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import com.sun.source.util.JavacTask;
+
+public class PluginsInModulesTest extends ModuleTestBase {
+
+    public static void main(String... args) throws Exception {
+        new PluginsInModulesTest().runTests();
+    }
+
+    private static final String pluginModule1 =
+            "module pluginMod1 {\n" +
+            "    requires jdk.compiler;\n" +
+            "\n" +
+            "    provides com.sun.source.util.Plugin\n" +
+            "      with mypkg1.SimplePlugin1;\n" +
+            "}";
+
+    private static final String plugin1 =
+            "package mypkg1;\n" +
+            "import com.sun.source.util.JavacTask;\n" +
+            "import com.sun.source.util.Plugin;\n" +
+            "import com.sun.source.util.TaskEvent;\n" +
+            "import com.sun.source.util.TaskListener;\n" +
+            "\n" +
+            "public class SimplePlugin1 implements Plugin {\n" +
+            "\n" +
+            "    @Override\n" +
+            "    public String getName() {\n" +
+            "        return \"simpleplugin1\";\n" +
+            "    }\n" +
+            "\n" +
+            "    @Override\n" +
+            "    public void init(JavacTask task, String... args) {\n" +
+            "        task.addTaskListener(new PostAnalyzeTaskListener());\n" +
+            "    }\n" +
+            "\n" +
+            "    private static class PostAnalyzeTaskListener implements TaskListener {\n" +
+            "        @Override\n" +
+            "        public void started(TaskEvent taskEvent) { \n" +
+            "            if (taskEvent.getKind().equals(TaskEvent.Kind.COMPILATION)) {\n" +
+            "                System.out.println(\"simpleplugin1 started for event \" + taskEvent.getKind());\n" +
+            "            }\n" +
+            "        }\n" +
+            "\n" +
+            "        @Override\n" +
+            "        public void finished(TaskEvent taskEvent) {\n" +
+            "            if (taskEvent.getKind().equals(TaskEvent.Kind.COMPILATION)) {\n" +
+            "                System.out.println(\"simpleplugin1 finished for event \" + taskEvent.getKind());\n" +
+            "            }\n" +
+            "        }\n" +
+            "    }\n" +
+            "}";
+
+    private static final String testClass = "class Test{}";
+
+    void initialization(Path base) throws Exception {
+        moduleSrc = base.resolve("plugin_mods_src");
+        Path pluginMod1 = moduleSrc.resolve("pluginMod1");
+
+        processorCompiledModules = base.resolve("mods");
+
+        Files.createDirectories(processorCompiledModules);
+
+        tb.writeJavaFiles(
+                pluginMod1,
+                pluginModule1,
+                plugin1);
+
+        String log = tb.new JavacTask()
+                .options("-modulesourcepath", moduleSrc.toString())
+                .outdir(processorCompiledModules)
+                .files(findJavaFiles(moduleSrc))
+                .run()
+                .writeAll()
+                .getOutput(ToolBox.OutputKind.DIRECT);
+
+        if (!log.isEmpty()) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+
+        classes = base.resolve("classes");
+        Files.createDirectories(classes);
+    }
+
+    Path processorCompiledModules;
+    Path moduleSrc;
+    Path classes;
+
+    @Test
+    void testUseOnlyOneProcessor(Path base) throws Exception {
+        initialization(base);
+        String log = tb.new JavacTask()
+                .options("-processormodulepath", processorCompiledModules.toString(),
+                        "-Xplugin:simpleplugin1")
+                .outdir(classes)
+                .sources(testClass)
+                .run()
+                .writeAll()
+                .getOutput(ToolBox.OutputKind.STDOUT);
+        if (!log.trim().equals("simpleplugin1 started for event COMPILATION\n" +
+                               "simpleplugin1 finished for event COMPILATION")) {
+            throw new AssertionError("Unexpected output: " + log);
+        }
+    }
+}
--- a/test/tools/javac/plugin/showtype/Test.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/test/tools/javac/plugin/showtype/Test.java	Fri Oct 02 11:40:25 2015 -0700
@@ -34,23 +34,17 @@
  */
 
 import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
+
 import javax.tools.JavaCompiler;
-import javax.tools.JavaFileManager;
 import javax.tools.JavaFileObject;
 import javax.tools.StandardJavaFileManager;
 import javax.tools.StandardLocation;
 import javax.tools.ToolProvider;
 
+import com.sun.source.util.JavacTask;
+
 public class Test {
     public static void main(String... args) throws Exception {
         new Test().run();
--- a/test/tools/lib/ToolBox.java	Thu Oct 01 16:55:39 2015 +0100
+++ b/test/tools/lib/ToolBox.java	Fri Oct 02 11:40:25 2015 -0700
@@ -535,13 +535,17 @@
         }
 
         /**
-         * Returns the content of a named stream as a list of lines.
-         * @param outputKind the kind of the selected stream
-         * @return the content that was written to that stream when the tool
+         * Returns the content of named streams as a list of lines.
+         * @param outputKinds the kinds of the selected streams
+         * @return the content that was written to the given streams when the tool
          *  was executed.
          */
-        public List<String> getOutputLines(OutputKind outputKind) {
-            return Arrays.asList(outputMap.get(outputKind).split(lineSeparator));
+        public List<String> getOutputLines(OutputKind... outputKinds) {
+            List<String> result = new ArrayList<>();
+            for (OutputKind outputKind : outputKinds) {
+                result.addAll(Arrays.asList(outputMap.get(outputKind).split(lineSeparator)));
+            }
+            return result;
         }
 
         /**