View Javadoc
1   package org.codehaus.plexus.compiler.javac;
2   /*
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   */
20  
21  import org.codehaus.plexus.compiler.CompilerConfiguration;
22  import org.codehaus.plexus.compiler.CompilerMessage;
23  import org.codehaus.plexus.compiler.CompilerException;
24  import org.codehaus.plexus.compiler.CompilerResult;
25  
26  import javax.tools.Diagnostic;
27  import javax.tools.DiagnosticCollector;
28  import javax.tools.JavaCompiler;
29  import javax.tools.JavaFileObject;
30  import javax.tools.StandardJavaFileManager;
31  import javax.tools.ToolProvider;
32  import java.nio.charset.Charset;
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.Collections;
36  import java.util.List;
37  import java.util.concurrent.CopyOnWriteArrayList;
38  
39  /**
40   * @author Olivier Lamy
41   * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
42   * @since 2.0
43   */
44  public class JavaxToolsCompiler
45  {
46      /**
47       * is that thread safe ???
48       */
49      @SuppressWarnings( "restriction" )
50      static final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler();
51  
52      private static List<JavaCompiler> JAVA_COMPILERS = new CopyOnWriteArrayList<>();
53  
54      protected static JavaCompiler getJavaCompiler( CompilerConfiguration compilerConfiguration )
55      {
56          switch ( compilerConfiguration.getCompilerReuseStrategy() )
57          {
58              case AlwaysNew:
59                  return ToolProvider.getSystemJavaCompiler();
60              case ReuseCreated:
61                  JavaCompiler javaCompiler;
62                  synchronized ( JAVA_COMPILERS )
63                  {
64                      if ( JAVA_COMPILERS.size() > 0 )
65                      {
66                          javaCompiler = JAVA_COMPILERS.get( 0 );
67                          JAVA_COMPILERS.remove( javaCompiler );
68                          return javaCompiler;
69                      }
70                  }
71                  javaCompiler = ToolProvider.getSystemJavaCompiler();
72                  return javaCompiler;
73              case ReuseSame:
74              default:
75                  return COMPILER;
76          }
77  
78      }
79  
80      static void releaseJavaCompiler( JavaCompiler javaCompiler, CompilerConfiguration compilerConfiguration )
81      {
82          if ( javaCompiler == null )
83          {
84              return;
85          }
86          if ( compilerConfiguration.getCompilerReuseStrategy()
87              == CompilerConfiguration.CompilerReuseStrategy.ReuseCreated )
88          {
89              JAVA_COMPILERS.add( javaCompiler );
90          }
91      }
92  
93      static CompilerResult compileInProcess( String[] args, final CompilerConfiguration config, String[] sourceFiles )
94          throws CompilerException
95      {
96          JavaCompiler compiler = getJavaCompiler( config );
97          try
98          {
99              if ( compiler == null )
100             {
101                 CompilerMessage message = new CompilerMessage( "No compiler is provided in this environment. "
102                                                                    + "Perhaps you are running on a JRE rather than a JDK?",
103                                                                CompilerMessage.Kind.ERROR );
104                 return new CompilerResult( false, Collections.singletonList( message ) );
105             }
106             final String sourceEncoding = config.getSourceEncoding();
107             final Charset sourceCharset = sourceEncoding == null ? null : Charset.forName( sourceEncoding );
108             final DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<JavaFileObject>();
109             final StandardJavaFileManager standardFileManager =
110                 compiler.getStandardFileManager( collector, null, sourceCharset );
111 
112             final Iterable<? extends JavaFileObject> fileObjects =
113                 standardFileManager.getJavaFileObjectsFromStrings( Arrays.asList( sourceFiles ) );
114 
115              /*(Writer out,
116              JavaFileManager fileManager,
117              DiagnosticListener<? super JavaFileObject> diagnosticListener,
118              Iterable<String> options,
119              Iterable<String> classes,
120              Iterable<? extends JavaFileObject> compilationUnits)*/
121 
122             List<String> arguments = Arrays.asList( args );
123 
124             final JavaCompiler.CompilationTask task =
125                 compiler.getTask( null, standardFileManager, collector, arguments, null, fileObjects );
126             final Boolean result = task.call();
127             final ArrayList<CompilerMessage> compilerMsgs = new ArrayList<CompilerMessage>();
128             for ( Diagnostic<? extends JavaFileObject> diagnostic : collector.getDiagnostics() )
129             {
130                 CompilerMessage.Kind kind = convertKind(diagnostic);
131                 String baseMessage = diagnostic.getMessage( null );
132                 if ( baseMessage == null )
133                 {
134                     continue;
135                 }
136                 JavaFileObject source = diagnostic.getSource();
137                 String longFileName = source == null ? null : source.toUri().getPath();
138                 String shortFileName = source == null ? null : source.getName();
139                 String formattedMessage = baseMessage;
140                 int lineNumber = Math.max( 0, (int) diagnostic.getLineNumber() );
141                 int columnNumber = Math.max( 0, (int) diagnostic.getColumnNumber() );
142                 if ( source != null && lineNumber > 0 )
143                 {
144                     // Some compilers like to copy the file name into the message, which makes it appear twice.
145                     String possibleTrimming = longFileName + ":" + lineNumber + ": ";
146                     if ( formattedMessage.startsWith( possibleTrimming ) )
147                     {
148                         formattedMessage = formattedMessage.substring( possibleTrimming.length() );
149                     }
150                     else
151                     {
152                         possibleTrimming = shortFileName + ":" + lineNumber + ": ";
153                         if ( formattedMessage.startsWith( possibleTrimming ) )
154                         {
155                             formattedMessage = formattedMessage.substring( possibleTrimming.length() );
156                         }
157                     }
158                 }
159                 compilerMsgs.add(
160                     new CompilerMessage( longFileName, kind, lineNumber, columnNumber, lineNumber, columnNumber,
161                                          formattedMessage ) );
162             }
163             if ( result != Boolean.TRUE && compilerMsgs.isEmpty() )
164             {
165                 compilerMsgs.add(
166                     new CompilerMessage( "An unknown compilation problem occurred", CompilerMessage.Kind.ERROR ) );
167             }
168 
169             return new CompilerResult( result, compilerMsgs );
170         }
171         catch ( Exception e )
172         {
173             throw new CompilerException( e.getMessage(), e );
174         }
175         finally
176         {
177             releaseJavaCompiler( compiler, config );
178 
179         }
180     }
181 
182     public static CompilerMessage.Kind convertKind(Diagnostic<? extends JavaFileObject> diagnostic) {
183         CompilerMessage.Kind kind;
184         switch ( diagnostic.getKind() )
185         {
186             case ERROR:
187                 kind = CompilerMessage.Kind.ERROR;
188                 break;
189             case WARNING:
190                 kind = CompilerMessage.Kind.WARNING;
191                 break;
192             case MANDATORY_WARNING:
193                 kind = CompilerMessage.Kind.MANDATORY_WARNING;
194                 break;
195             case NOTE:
196                 kind = CompilerMessage.Kind.NOTE;
197                 break;
198             default:
199                 kind = CompilerMessage.Kind.OTHER;
200                 break;
201         }
202         return kind;
203     }
204 }