1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.databind.codegen;
28
29 import gov.nist.secauto.metaschema.core.model.IModule;
30 import gov.nist.secauto.metaschema.databind.IBindingContext;
31 import gov.nist.secauto.metaschema.databind.codegen.config.DefaultBindingConfiguration;
32 import gov.nist.secauto.metaschema.databind.codegen.config.IBindingConfiguration;
33
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36
37 import java.io.IOException;
38 import java.lang.module.ModuleDescriptor;
39 import java.net.MalformedURLException;
40 import java.net.URL;
41 import java.net.URLClassLoader;
42 import java.nio.file.Path;
43 import java.security.AccessController;
44 import java.security.PrivilegedAction;
45 import java.util.ArrayList;
46 import java.util.LinkedList;
47 import java.util.List;
48 import java.util.stream.Collectors;
49
50 import javax.tools.DiagnosticCollector;
51 import javax.tools.JavaCompiler;
52 import javax.tools.JavaFileManager;
53 import javax.tools.JavaFileObject;
54 import javax.tools.StandardJavaFileManager;
55 import javax.tools.ToolProvider;
56
57 import edu.umd.cs.findbugs.annotations.NonNull;
58
59
60
61
62
63
64
65 public final class ModuleCompilerHelper {
66 private static final Logger LOGGER = LogManager.getLogger(ModuleCompilerHelper.class);
67
68 private ModuleCompilerHelper() {
69
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @NonNull
86 public static IProduction compileMetaschema(
87 @NonNull IModule module,
88 @NonNull Path classDir)
89 throws IOException {
90 return compileModule(module, classDir, new DefaultBindingConfiguration());
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 @NonNull
110 public static IProduction compileModule(
111 @NonNull IModule module,
112 @NonNull Path classDir,
113 @NonNull IBindingConfiguration bindingConfiguration) throws IOException {
114 IProduction production = JavaGenerator.generate(module, classDir, bindingConfiguration);
115 List<IGeneratedClass> classesToCompile = production.getGeneratedClasses().collect(Collectors.toList());
116
117 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
118 if (!compileGeneratedClasses(classesToCompile, diagnostics, classDir)) {
119 if (LOGGER.isErrorEnabled()) {
120 LOGGER.error(diagnostics.getDiagnostics().toString());
121 }
122 throw new IllegalStateException(String.format("failed to compile classes: %s",
123 classesToCompile.stream()
124 .map(clazz -> clazz.getClassName().canonicalName())
125 .collect(Collectors.joining(","))));
126 }
127 return production;
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 @SuppressWarnings("null")
142 @NonNull
143 public static ClassLoader newClassLoader(
144 @NonNull final Path classDir,
145 @NonNull final ClassLoader parent) {
146 return AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
147 @Override
148 public URLClassLoader run() {
149 try {
150 return new URLClassLoader(new URL[] { classDir.toUri().toURL() }, parent);
151 } catch (MalformedURLException ex) {
152 throw new IllegalStateException("unable to configure class loader", ex);
153 }
154 }
155 });
156 }
157
158 @SuppressWarnings({
159 "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity",
160 })
161 private static boolean compile(
162 JavaCompiler compiler,
163 JavaFileManager fileManager,
164 DiagnosticCollector<JavaFileObject> diagnostics,
165 List<JavaFileObject> compilationUnits,
166 Path classDir) {
167
168 String moduleName = null;
169 Module module = IBindingContext.class.getModule();
170 if (module != null) {
171 ModuleDescriptor descriptor = module.getDescriptor();
172 if (descriptor != null) {
173
174 moduleName = descriptor.name();
175 }
176 }
177
178 List<String> options = new LinkedList<>();
179
180
181 options.add("-d");
182 options.add(classDir.toString());
183
184 String classPath = System.getProperty("java.class.path");
185 String modulePath = System.getProperty("jdk.module.path");
186 if (moduleName == null) {
187
188 String path = null;
189 if (classPath != null) {
190 path = classPath;
191 }
192
193 if (modulePath != null) {
194 path = path == null ? modulePath : path + ":" + modulePath;
195 }
196
197 if (path != null) {
198 options.add("-classpath");
199 options.add(path);
200 }
201 } else {
202
203 if (classPath != null) {
204 options.add("-classpath");
205 options.add(classPath);
206 }
207
208 if (modulePath != null) {
209 options.add("-p");
210 options.add(modulePath);
211 }
212 }
213
214 if (LOGGER.isDebugEnabled()) {
215 LOGGER.atDebug().log("Using options: {}", options);
216 }
217
218 JavaCompiler.CompilationTask task
219 = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
220
221 if (moduleName != null) {
222 task.addModules(List.of(moduleName));
223 }
224 return task.call();
225 }
226
227 private static boolean compileGeneratedClasses(
228 List<IGeneratedClass> classesToCompile,
229 DiagnosticCollector<JavaFileObject> diagnostics,
230 Path classDir) throws IOException {
231 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
232
233 try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
234
235 List<JavaFileObject> compilationUnits = new ArrayList<>(classesToCompile.size());
236 for (IGeneratedClass generatedClass : classesToCompile) {
237 compilationUnits.add(fileManager.getJavaFileObjects(generatedClass.getClassFile()).iterator().next());
238 }
239
240 return compile(compiler, fileManager, diagnostics, compilationUnits, classDir);
241 }
242 }
243 }