1 package org.codehaus.plexus.compiler.javac;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import javax.inject.Named;
22 import javax.tools.Diagnostic;
23 import javax.tools.DiagnosticCollector;
24 import javax.tools.JavaCompiler;
25 import javax.tools.JavaFileObject;
26 import javax.tools.StandardJavaFileManager;
27 import javax.tools.ToolProvider;
28
29 import java.nio.charset.Charset;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.concurrent.CopyOnWriteArrayList;
36
37 import org.codehaus.plexus.compiler.CompilerConfiguration;
38 import org.codehaus.plexus.compiler.CompilerException;
39 import org.codehaus.plexus.compiler.CompilerMessage;
40 import org.codehaus.plexus.compiler.CompilerResult;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45
46
47
48
49 @Named
50 public class JavaxToolsCompiler implements InProcessCompiler {
51 private final Logger log = LoggerFactory.getLogger(getClass());
52
53
54
55 @SuppressWarnings("restriction")
56 private final JavaCompiler COMPILER = newJavaCompiler();
57
58 protected JavaCompiler newJavaCompiler() {
59 return ToolProvider.getSystemJavaCompiler();
60 }
61
62 private final List<JavaCompiler> JAVA_COMPILERS = new CopyOnWriteArrayList<>();
63
64 private JavaCompiler getJavaCompiler(CompilerConfiguration compilerConfiguration) {
65 switch (compilerConfiguration.getCompilerReuseStrategy()) {
66 case AlwaysNew:
67 return newJavaCompiler();
68 case ReuseCreated:
69 JavaCompiler javaCompiler;
70 synchronized (JAVA_COMPILERS) {
71 if (JAVA_COMPILERS.size() > 0) {
72 javaCompiler = JAVA_COMPILERS.get(0);
73 JAVA_COMPILERS.remove(javaCompiler);
74 return javaCompiler;
75 }
76 }
77 javaCompiler = newJavaCompiler();
78 return javaCompiler;
79 case ReuseSame:
80 default:
81 return COMPILER;
82 }
83 }
84
85 private void releaseJavaCompiler(JavaCompiler javaCompiler, CompilerConfiguration compilerConfiguration) {
86 if (javaCompiler == null) {
87 return;
88 }
89 if (compilerConfiguration.getCompilerReuseStrategy()
90 == CompilerConfiguration.CompilerReuseStrategy.ReuseCreated) {
91 JAVA_COMPILERS.add(javaCompiler);
92 }
93 }
94
95 public CompilerResult compileInProcess(String[] args, final CompilerConfiguration config, String[] sourceFiles)
96 throws CompilerException {
97 JavaCompiler compiler = getJavaCompiler(config);
98 try {
99 if (compiler == null) {
100 CompilerMessage message = new CompilerMessage(
101 "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 String sourceEncoding = config.getSourceEncoding();
107 Charset sourceCharset = sourceEncoding == null ? null : Charset.forName(sourceEncoding);
108 DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
109 try (StandardJavaFileManager standardFileManager =
110 compiler.getStandardFileManager(collector, null, sourceCharset)) {
111
112 Iterable<? extends JavaFileObject> fileObjects =
113 standardFileManager.getJavaFileObjectsFromStrings(Arrays.asList(sourceFiles));
114
115
116
117
118
119
120
121
122 List<String> arguments = Arrays.asList(args);
123
124 JavaCompiler.CompilationTask task =
125 compiler.getTask(null, standardFileManager, collector, arguments, null, fileObjects);
126 Boolean result = task.call();
127 List<CompilerMessage> compilerMsgs = new ArrayList<>();
128
129 for (Diagnostic<? extends JavaFileObject> diagnostic : collector.getDiagnostics()) {
130 CompilerMessage.Kind kind = convertKind(diagnostic);
131
132 String baseMessage;
133 try {
134 baseMessage = diagnostic.getMessage(Locale.getDefault());
135 } catch (Throwable e)
136 {
137
138
139 log.debug(
140 "Ignore Issue get JavaCompiler Diagnostic message (see https://bugs.openjdk.java.net/browse/JDK-8210649):"
141 + e.getMessage(),
142 e);
143
144
145 baseMessage = diagnostic.toString();
146 }
147 if (baseMessage == null) {
148 continue;
149 }
150 JavaFileObject source = diagnostic.getSource();
151 String longFileName = source == null ? null : source.toUri().getPath();
152 String shortFileName = source == null ? null : source.getName();
153 String formattedMessage = baseMessage;
154 int lineNumber = Math.max(0, (int) diagnostic.getLineNumber());
155 int columnNumber = Math.max(0, (int) diagnostic.getColumnNumber());
156 if (source != null && lineNumber > 0) {
157
158 String possibleTrimming = longFileName + ":" + lineNumber + ": ";
159 if (formattedMessage.startsWith(possibleTrimming)) {
160 formattedMessage = formattedMessage.substring(possibleTrimming.length());
161 } else {
162 possibleTrimming = shortFileName + ":" + lineNumber + ": ";
163 if (formattedMessage.startsWith(possibleTrimming)) {
164 formattedMessage = formattedMessage.substring(possibleTrimming.length());
165 }
166 }
167 }
168 compilerMsgs.add(new CompilerMessage(
169 longFileName, kind, lineNumber, columnNumber, lineNumber, columnNumber, formattedMessage));
170 }
171 if (result != Boolean.TRUE && compilerMsgs.isEmpty()) {
172 compilerMsgs.add(
173 new CompilerMessage("An unknown compilation problem occurred", CompilerMessage.Kind.ERROR));
174 }
175
176 return new CompilerResult(result, compilerMsgs);
177 }
178 } catch (Exception e) {
179 throw new CompilerException(e.getMessage(), e);
180 } finally {
181 releaseJavaCompiler(compiler, config);
182 }
183 }
184
185 private CompilerMessage.Kind convertKind(Diagnostic<? extends JavaFileObject> diagnostic) {
186 CompilerMessage.Kind kind;
187 switch (diagnostic.getKind()) {
188 case ERROR:
189 kind = CompilerMessage.Kind.ERROR;
190 break;
191 case WARNING:
192 kind = CompilerMessage.Kind.WARNING;
193 break;
194 case MANDATORY_WARNING:
195 kind = CompilerMessage.Kind.MANDATORY_WARNING;
196 break;
197 case NOTE:
198 kind = CompilerMessage.Kind.NOTE;
199 break;
200 default:
201 kind = CompilerMessage.Kind.OTHER;
202 break;
203 }
204 return kind;
205 }
206 }