1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin;
20
21 import javax.inject.Named;
22 import javax.inject.Singleton;
23
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.concurrent.ConcurrentHashMap;
30
31 import org.apache.maven.RepositoryUtils;
32 import org.apache.maven.artifact.Artifact;
33 import org.apache.maven.model.Plugin;
34 import org.apache.maven.project.MavenProject;
35 import org.codehaus.plexus.classworlds.realm.ClassRealm;
36 import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
37 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
38 import org.eclipse.aether.RepositorySystemSession;
39 import org.eclipse.aether.graph.DependencyFilter;
40 import org.eclipse.aether.repository.LocalRepository;
41 import org.eclipse.aether.repository.RemoteRepository;
42 import org.eclipse.aether.repository.WorkspaceRepository;
43
44
45
46
47 @Named
48 @Singleton
49 public class DefaultPluginRealmCache implements PluginRealmCache, Disposable {
50
51
52
53 protected static class CacheKey implements Key {
54
55 private final Plugin plugin;
56
57 private final WorkspaceRepository workspace;
58
59 private final LocalRepository localRepo;
60
61 private final List<RemoteRepository> repositories;
62
63 private final ClassLoader parentRealm;
64
65 private final Map<String, ClassLoader> foreignImports;
66
67 private final DependencyFilter filter;
68
69 private final int hashCode;
70
71 public CacheKey(
72 Plugin plugin,
73 ClassLoader parentRealm,
74 Map<String, ClassLoader> foreignImports,
75 DependencyFilter dependencyFilter,
76 List<RemoteRepository> repositories,
77 RepositorySystemSession session) {
78 this.plugin = plugin.clone();
79 this.workspace = RepositoryUtils.getWorkspace(session);
80 this.localRepo = session.getLocalRepository();
81 this.repositories = new ArrayList<>(repositories.size());
82 for (RemoteRepository repository : repositories) {
83 if (repository.isRepositoryManager()) {
84 this.repositories.addAll(repository.getMirroredRepositories());
85 } else {
86 this.repositories.add(repository);
87 }
88 }
89 this.parentRealm = parentRealm;
90 this.foreignImports = (foreignImports != null) ? foreignImports : Collections.emptyMap();
91 this.filter = dependencyFilter;
92
93 int hash = 17;
94 hash = hash * 31 + CacheUtils.pluginHashCode(plugin);
95 hash = hash * 31 + Objects.hashCode(workspace);
96 hash = hash * 31 + Objects.hashCode(localRepo);
97 hash = hash * 31 + RepositoryUtils.repositoriesHashCode(repositories);
98 hash = hash * 31 + Objects.hashCode(parentRealm);
99 hash = hash * 31 + this.foreignImports.hashCode();
100 hash = hash * 31 + Objects.hashCode(dependencyFilter);
101 this.hashCode = hash;
102 }
103
104 @Override
105 public String toString() {
106 return plugin.getId();
107 }
108
109 @Override
110 public int hashCode() {
111 return hashCode;
112 }
113
114 @Override
115 public boolean equals(Object o) {
116 if (o == this) {
117 return true;
118 }
119
120 if (o instanceof CacheKey that) {
121 return parentRealm == that.parentRealm
122 && CacheUtils.pluginEquals(plugin, that.plugin)
123 && Objects.equals(workspace, that.workspace)
124 && Objects.equals(localRepo, that.localRepo)
125 && RepositoryUtils.repositoriesEquals(this.repositories, that.repositories)
126 && Objects.equals(filter, that.filter)
127 && Objects.equals(foreignImports, that.foreignImports);
128 } else {
129 return false;
130 }
131 }
132 }
133
134 protected final Map<Key, CacheRecord> cache = new ConcurrentHashMap<>();
135
136 @Override
137 public Key createKey(
138 Plugin plugin,
139 ClassLoader parentRealm,
140 Map<String, ClassLoader> foreignImports,
141 DependencyFilter dependencyFilter,
142 List<RemoteRepository> repositories,
143 RepositorySystemSession session) {
144 return new CacheKey(plugin, parentRealm, foreignImports, dependencyFilter, repositories, session);
145 }
146
147 @Override
148 public CacheRecord get(Key key) {
149 return cache.get(key);
150 }
151
152 @Override
153 public CacheRecord get(Key key, PluginRealmSupplier supplier)
154 throws PluginResolutionException, PluginContainerException {
155 try {
156 return cache.computeIfAbsent(key, k -> {
157 try {
158 return supplier.load();
159 } catch (PluginResolutionException | PluginContainerException e) {
160 throw new RuntimeException(e);
161 }
162 });
163 } catch (RuntimeException e) {
164 if (e.getCause() instanceof PluginResolutionException pluginResolutionException) {
165 throw pluginResolutionException;
166 }
167 if (e.getCause() instanceof PluginContainerException pluginContainerException) {
168 throw pluginContainerException;
169 }
170 throw e;
171 }
172 }
173
174 @Override
175 public CacheRecord put(Key key, ClassRealm pluginRealm, List<Artifact> pluginArtifacts) {
176 Objects.requireNonNull(pluginRealm, "pluginRealm cannot be null");
177 Objects.requireNonNull(pluginArtifacts, "pluginArtifacts cannot be null");
178
179 if (cache.containsKey(key)) {
180 throw new IllegalStateException("Duplicate plugin realm for plugin " + key);
181 }
182
183 CacheRecord record = new CacheRecord(pluginRealm, pluginArtifacts);
184
185 cache.put(key, record);
186
187 return record;
188 }
189
190 @Override
191 public void flush() {
192 for (CacheRecord record : cache.values()) {
193 ClassRealm realm = record.getRealm();
194 try {
195 realm.getWorld().disposeRealm(realm.getId());
196 } catch (NoSuchRealmException e) {
197
198 }
199 }
200 cache.clear();
201 }
202
203 protected static int pluginHashCode(Plugin plugin) {
204 return CacheUtils.pluginHashCode(plugin);
205 }
206
207 protected static boolean pluginEquals(Plugin a, Plugin b) {
208 return CacheUtils.pluginEquals(a, b);
209 }
210
211 @Override
212 public void register(MavenProject project, Key key, CacheRecord record) {
213
214 }
215
216 @Override
217 public void dispose() {
218 flush();
219 }
220 }