View Javadoc
1   /*
2    * Copyright 2011 Google Inc. All Rights Reserved.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.codehaus.plexus.compiler.javac.errorprone;
18  
19  import javax.inject.Named;
20  import javax.tools.JavaCompiler;
21  
22  import java.net.MalformedURLException;
23  import java.net.URL;
24  import java.net.URLClassLoader;
25  
26  import com.google.errorprone.ErrorProneJavaCompiler;
27  import org.codehaus.plexus.compiler.CompilerConfiguration;
28  import org.codehaus.plexus.compiler.CompilerException;
29  import org.codehaus.plexus.compiler.CompilerMessage;
30  import org.codehaus.plexus.compiler.CompilerResult;
31  import org.codehaus.plexus.compiler.javac.InProcessCompiler;
32  import org.codehaus.plexus.compiler.javac.JavacCompiler;
33  import org.codehaus.plexus.compiler.javac.JavaxToolsCompiler;
34  
35  /**
36   * This class overrides JavacCompiler with modifications to use the error-prone
37   * entry point into Javac.
38   *
39   * @author <a href="mailto:alexeagle@google.com">Alex Eagle</a>
40   */
41  @Named("javac-with-errorprone")
42  public class JavacCompilerWithErrorProne extends JavacCompiler {
43      @Override
44      public String getCompilerId() {
45          return "javac-with-errorprone";
46      }
47  
48      private static class NonDelegatingClassLoader extends URLClassLoader {
49          ClassLoader original;
50  
51          public NonDelegatingClassLoader(URL[] urls, ClassLoader original) throws MalformedURLException {
52              super(urls, null);
53              this.original = original;
54          }
55  
56          @Override
57          public Class<?> loadClass(String name, boolean complete) throws ClassNotFoundException {
58              // Classes loaded inside CompilerInvoker that need to reach back to the caller
59              if (name.contentEquals(CompilerResult.class.getName())
60                      || name.contentEquals(InProcessCompiler.class.getName())
61                      || name.contentEquals(CompilerConfiguration.class.getName())
62                      || name.contentEquals(CompilerConfiguration.CompilerReuseStrategy.class.getName())
63                      || name.contentEquals(CompilerException.class.getName())
64                      || name.contentEquals(CompilerMessage.class.getName())
65                      || name.contentEquals(CompilerMessage.Kind.class.getName())) {
66                  return original.loadClass(name);
67              }
68  
69              try {
70                  synchronized (getClassLoadingLock(name)) {
71                      Class c = findLoadedClass(name);
72                      if (c != null) {
73                          return c;
74                      }
75                      return findClass(name);
76                  }
77              } catch (ClassNotFoundException e) {
78                  return super.loadClass(name, complete);
79              }
80          }
81      }
82  
83      protected InProcessCompiler inProcessCompiler() {
84          if (Thread.currentThread().getContextClassLoader().getResource("java/lang/module/ModuleReference.class")
85                  == null) {
86              ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
87              URL[] urls = ((URLClassLoader) contextClassLoader).getURLs();
88              ClassLoader loader;
89              try {
90                  loader = new NonDelegatingClassLoader(urls, contextClassLoader);
91                  Class<?> clazz = Class.forName(CompilerInvoker.class.getName(), true, loader);
92                  return (InProcessCompiler) clazz.newInstance();
93              } catch (Exception e) {
94                  throw new IllegalStateException(e);
95              }
96          }
97          return new CompilerInvoker();
98      }
99  
100     /**
101      * A wrapper for all of the error-prone specific classes. Loading this class with a
102      * non-delegating classloader ensures that error-prone's javac loads javax.tools.* classes from
103      * javac.jar instead of from the bootclasspath.
104      */
105     public static class CompilerInvoker extends JavaxToolsCompiler {
106         @Override
107         protected JavaCompiler newJavaCompiler() {
108             return new ErrorProneJavaCompiler();
109         }
110     }
111 }