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