1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.classrealm;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.net.MalformedURLException;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.Random;
33 import java.util.Set;
34 import java.util.TreeMap;
35
36 import org.apache.maven.artifact.ArtifactUtils;
37 import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
38 import org.apache.maven.extension.internal.CoreExports;
39 import org.apache.maven.internal.CoreRealm;
40 import org.apache.maven.model.Model;
41 import org.apache.maven.model.Plugin;
42 import org.codehaus.plexus.classworlds.ClassWorld;
43 import org.codehaus.plexus.classworlds.realm.ClassRealm;
44 import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
45 import org.eclipse.aether.artifact.Artifact;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49
50
51
52
53
54
55 @Named
56 @Singleton
57 public class DefaultClassRealmManager implements ClassRealmManager {
58 public static final String API_REALMID = "maven.api";
59
60
61
62
63
64
65
66
67
68 private static final ClassLoader PARENT_CLASSLOADER = ClassWorld.class.getClassLoader();
69
70 private final Logger logger = LoggerFactory.getLogger(getClass());
71
72 private final ClassWorld world;
73
74 private final ClassRealm containerRealm;
75
76
77 private final List<ClassRealmManagerDelegate> delegates;
78
79 private final ClassRealm mavenApiRealm;
80
81
82
83
84
85 private final Set<String> providedArtifacts;
86
87 @Inject
88 public DefaultClassRealmManager(
89 CoreRealm coreRealm, List<ClassRealmManagerDelegate> delegates, CoreExports exports) {
90 this.world = coreRealm.getClassWorld();
91 this.containerRealm = coreRealm.getRealm();
92 this.delegates = delegates;
93
94 Map<String, ClassLoader> foreignImports = exports.getExportedPackages();
95
96 this.mavenApiRealm = createRealm(
97 API_REALMID,
98 RealmType.Core,
99 null ,
100 null ,
101 foreignImports,
102 null );
103
104 this.providedArtifacts = exports.getExportedArtifacts();
105 }
106
107 private ClassRealm newRealm(String id) {
108 synchronized (world) {
109 String realmId = id;
110
111 Random random = new Random();
112
113 while (true) {
114 try {
115 ClassRealm classRealm = world.newRealm(realmId, null);
116
117 logger.debug("Created new class realm {}", realmId);
118
119 return classRealm;
120 } catch (DuplicateRealmException e) {
121 realmId = id + '-' + random.nextInt();
122 }
123 }
124 }
125 }
126
127 public ClassRealm getMavenApiRealm() {
128 return mavenApiRealm;
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142
143 private ClassRealm createRealm(
144 String baseRealmId,
145 RealmType type,
146 ClassLoader parent,
147 List<String> parentImports,
148 Map<String, ClassLoader> foreignImports,
149 List<Artifact> artifacts) {
150 List<ClassRealmConstituent> constituents = new ArrayList<>(artifacts == null ? 0 : artifacts.size());
151
152 if (artifacts != null && !artifacts.isEmpty()) {
153 for (Artifact artifact : artifacts) {
154 if (!isProvidedArtifact(artifact) && artifact.getFile() != null) {
155 constituents.add(new ArtifactClassRealmConstituent(artifact));
156 } else if (logger.isDebugEnabled()) {
157 logger.debug(" Excluded: {}", getId(artifact));
158 }
159 }
160 }
161
162 if (parentImports != null) {
163 parentImports = new ArrayList<>(parentImports);
164 } else {
165 parentImports = new ArrayList<>();
166 }
167
168 if (foreignImports != null) {
169 foreignImports = new TreeMap<>(foreignImports);
170 } else {
171 foreignImports = new TreeMap<>();
172 }
173
174 ClassRealm classRealm = newRealm(baseRealmId);
175
176 if (parent != null) {
177 classRealm.setParentClassLoader(parent);
178 }
179
180 callDelegates(classRealm, type, parent, parentImports, foreignImports, constituents);
181
182 wireRealm(classRealm, parentImports, foreignImports);
183
184 populateRealm(classRealm, constituents);
185
186 return classRealm;
187 }
188
189 public ClassRealm getCoreRealm() {
190 return containerRealm;
191 }
192
193 public ClassRealm createProjectRealm(Model model, List<Artifact> artifacts) {
194 Objects.requireNonNull(model, "model cannot be null");
195
196 ClassLoader parent = getMavenApiRealm();
197
198 return createRealm(getKey(model), RealmType.Project, parent, null, null, artifacts);
199 }
200
201 private static String getKey(Model model) {
202 return "project>" + model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
203 }
204
205 public ClassRealm createExtensionRealm(Plugin plugin, List<Artifact> artifacts) {
206 Objects.requireNonNull(plugin, "plugin cannot be null");
207
208 Map<String, ClassLoader> foreignImports = Collections.singletonMap("", getMavenApiRealm());
209
210 return createRealm(
211 getKey(plugin, true), RealmType.Extension, PARENT_CLASSLOADER, null, foreignImports, artifacts);
212 }
213
214 private boolean isProvidedArtifact(Artifact artifact) {
215 return providedArtifacts.contains(artifact.getGroupId() + ":" + artifact.getArtifactId());
216 }
217
218 public ClassRealm createPluginRealm(
219 Plugin plugin,
220 ClassLoader parent,
221 List<String> parentImports,
222 Map<String, ClassLoader> foreignImports,
223 List<Artifact> artifacts) {
224 Objects.requireNonNull(plugin, "plugin cannot be null");
225
226 if (parent == null) {
227 parent = PARENT_CLASSLOADER;
228 }
229
230 return createRealm(getKey(plugin, false), RealmType.Plugin, parent, parentImports, foreignImports, artifacts);
231 }
232
233 private static String getKey(Plugin plugin, boolean extension) {
234 String version = ArtifactUtils.toSnapshotVersion(plugin.getVersion());
235 return (extension ? "extension>" : "plugin>") + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":"
236 + version;
237 }
238
239 private static String getId(Artifact artifact) {
240 return getId(
241 artifact.getGroupId(),
242 artifact.getArtifactId(),
243 artifact.getExtension(),
244 artifact.getClassifier(),
245 artifact.getBaseVersion());
246 }
247
248 private static String getId(ClassRealmConstituent constituent) {
249 return getId(
250 constituent.getGroupId(),
251 constituent.getArtifactId(),
252 constituent.getType(),
253 constituent.getClassifier(),
254 constituent.getVersion());
255 }
256
257 private static String getId(String gid, String aid, String type, String cls, String ver) {
258 return gid + ':' + aid + ':' + type + ((cls != null && !cls.isEmpty()) ? ':' + cls : "") + ':' + ver;
259 }
260
261 private void callDelegates(
262 ClassRealm classRealm,
263 RealmType type,
264 ClassLoader parent,
265 List<String> parentImports,
266 Map<String, ClassLoader> foreignImports,
267 List<ClassRealmConstituent> constituents) {
268 List<ClassRealmManagerDelegate> delegates = new ArrayList<>(this.delegates);
269
270 if (!delegates.isEmpty()) {
271 ClassRealmRequest request =
272 new DefaultClassRealmRequest(type, parent, parentImports, foreignImports, constituents);
273
274 for (ClassRealmManagerDelegate delegate : delegates) {
275 try {
276 delegate.setupRealm(classRealm, request);
277 } catch (Exception e) {
278 logger.error(
279 delegate.getClass().getName() + " failed to setup class realm " + classRealm + ": "
280 + e.getMessage(),
281 e);
282 }
283 }
284 }
285 }
286
287 private void populateRealm(ClassRealm classRealm, List<ClassRealmConstituent> constituents) {
288 logger.debug("Populating class realm {}", classRealm.getId());
289
290 for (ClassRealmConstituent constituent : constituents) {
291 File file = constituent.getFile();
292
293 if (logger.isDebugEnabled()) {
294 String id = getId(constituent);
295 logger.debug(" Included: {}", id);
296 }
297
298 try {
299 classRealm.addURL(file.toURI().toURL());
300 } catch (MalformedURLException e) {
301
302 logger.error(e.getMessage(), e);
303 }
304 }
305 }
306
307 private void wireRealm(ClassRealm classRealm, List<String> parentImports, Map<String, ClassLoader> foreignImports) {
308 if (foreignImports != null && !foreignImports.isEmpty()) {
309 logger.debug("Importing foreign packages into class realm {}", classRealm.getId());
310
311 for (Map.Entry<String, ClassLoader> entry : foreignImports.entrySet()) {
312 ClassLoader importedRealm = entry.getValue();
313 String imp = entry.getKey();
314
315 logger.debug(" Imported: {} < {}", imp, getId(importedRealm));
316
317 classRealm.importFrom(importedRealm, imp);
318 }
319 }
320
321 if (parentImports != null && !parentImports.isEmpty()) {
322 logger.debug("Importing parent packages into class realm {}", classRealm.getId());
323
324 for (String imp : parentImports) {
325 logger.debug(" Imported: {} < {}", imp, getId(classRealm.getParentClassLoader()));
326
327 classRealm.importFromParent(imp);
328 }
329 }
330 }
331
332 private static Object getId(ClassLoader classLoader) {
333 if (classLoader instanceof ClassRealm) {
334 return ((ClassRealm) classLoader).getId();
335 }
336 return classLoader;
337 }
338 }