Skip to content

Commit c98d9c3

Browse files
committed
fix #640
1 parent ace3838 commit c98d9c3

File tree

2 files changed

+39
-17
lines changed

2 files changed

+39
-17
lines changed

hotswap-agent-core/src/main/java/org/hotswap/agent/config/PluginManager.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.HashSet;
2828
import java.util.Map;
2929
import java.util.Set;
30+
import java.util.concurrent.locks.ReentrantLock;
3031

3132
import org.hotswap.agent.command.Scheduler;
3233
import org.hotswap.agent.command.impl.SchedulerImpl;
@@ -37,7 +38,7 @@
3738
import org.hotswap.agent.watch.WatcherFactory;
3839

3940
/**
40-
* The main agent plugin manager, well known singleton controller.
41+
* The main agent plugin manager, well-known singleton controller.
4142
*
4243
* @author Jiri Bubnik
4344
*/
@@ -46,7 +47,7 @@ public class PluginManager {
4647

4748
public static final String PLUGIN_PACKAGE = "org.hotswap.agent.plugin";
4849

49-
////////////////////////// MANAGER SINGLETON /////////////////////////////////////
50+
////////////////////////// MANAGER SINGLETON /////////////////////////////////////
5051

5152
// singleton instance
5253
private static PluginManager INSTANCE = new PluginManager();
@@ -68,6 +69,7 @@ private PluginManager() {
6869
private Instrumentation instrumentation;
6970

7071
private Object hotswapLock = new Object();
72+
private ReentrantLock initClassLoaderLock = new ReentrantLock();
7173

7274
////////////////////////// PLUGINS /////////////////////////////////////
7375

@@ -113,8 +115,8 @@ public boolean isPluginInitialized(String pluginClassName, ClassLoader classLoad
113115
/**
114116
* Initialize the singleton plugin manager.
115117
* <ul>
116-
* <li>Create new resource watcher using WatcherFactory and start it in separate thread.</li>
117-
* <li>Create new scheduler and start it in separate thread.</li>
118+
* <li>Create a new resource watcher using WatcherFactory and start it in separate thread.</li>
119+
* <li>Create a new scheduler and start it in separate thread.</li>
118120
* <li>Scan for plugins</li>
119121
* <li>Register HotswapTransformer with the javaagent instrumentation class</li>
120122
* </ul>
@@ -178,7 +180,8 @@ public void initClassLoader(ClassLoader classLoader, ProtectionDomain protection
178180

179181
// synchronize ClassLoader patching - multiple classloaders may be patched at the same time
180182
// and they may synchronize loading for security reasons and introduce deadlocks
181-
synchronized (this) {
183+
try {
184+
initClassLoaderLock.lock();
182185
if (classLoaderConfigurations.containsKey(classLoader))
183186
return;
184187

@@ -192,6 +195,8 @@ public void initClassLoader(ClassLoader classLoader, ProtectionDomain protection
192195
PluginConfiguration pluginConfiguration = new PluginConfiguration(getPluginConfiguration(getClass().getClassLoader()), classLoader, false);
193196
classLoaderConfigurations.put(classLoader, pluginConfiguration);
194197
pluginConfiguration.init();
198+
} finally {
199+
initClassLoaderLock.unlock();
195200
}
196201

197202
// call listeners
@@ -203,7 +208,7 @@ public void initClassLoader(ClassLoader classLoader, ProtectionDomain protection
203208
* Remove any classloader reference and close all plugin instances associated with classloader.
204209
* This method is called typically after webapp undeploy.
205210
*
206-
* @param classLoader the classloader to cleanup
211+
* @param classLoader the classloader to clean up
207212
*/
208213
public void closeClassLoader(ClassLoader classLoader) {
209214
pluginRegistry.closeClassLoader(classLoader);
@@ -325,4 +330,12 @@ public void scheduleHotswap(Map<Class<?>, byte[]> reloadMap, int timeout) {
325330
getScheduler().scheduleCommand(new ScheduledHotswapCommand(reloadMap), timeout);
326331
}
327332

333+
/**
334+
* Retrieves the lock used to synchronize initialization of the class loader for the plugin manager.
335+
*
336+
* @return the lock instance used for class loader initialization synchronization.
337+
*/
338+
public ReentrantLock getInitClassLoaderLock() {
339+
return initClassLoaderLock;
340+
}
328341
}

hotswap-agent-core/src/main/java/org/hotswap/agent/util/HotswapTransformer.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
/**
4242
* Java instrumentation transformer.
4343
* <p/>
44-
* The is the single instance of transformer registered by HotswapAgent. It will delegate to plugins to
44+
* This is the single instance of transformer registered by HotswapAgent. It will delegate to plugins to
4545
* do the transformer work.
4646
*
4747
* @author Jiri Bubnik
@@ -107,7 +107,7 @@ public List<Pattern> getExcludedClassLoaderPatterns() {
107107
* @param classLoader the classloader to which this transformation is associated
108108
* @param classNameRegexp regexp to match fully qualified class name.
109109
* Because "." is any character in regexp, this will match / in the transform method as well
110-
* (diffentence between java/lang/String and java.lang.String).
110+
* (difference between java/lang/String and java.lang.String).
111111
* @param transformer the transformer to be called for each class matching regexp.
112112
*/
113113
public void registerTransformer(ClassLoader classLoader, String classNameRegexp, HaClassFileTransformer transformer) {
@@ -184,7 +184,7 @@ public void closeClassLoader(ClassLoader classLoader) {
184184
* <p>It does not do the instrumentation itself, instead iterates registered transformers and compares
185185
* registration class regexp - if the regexp matches, the classloader is called.
186186
* <p/>
187-
* <p>Note that class bytes may be send to multiple transformers, but the order is not defined.
187+
* <p>Note that class bytes may be sent to multiple transformers, but the order is not defined.
188188
*
189189
* @see ClassFileTransformer#transform(ClassLoader, String, Class, java.security.ProtectionDomain, byte[])
190190
*/
@@ -204,7 +204,7 @@ public byte[] transform(final ClassLoader classLoader, String className, Class<?
204204
List<PluginClassFileTransformer> pluginTransformers = new ArrayList<>();
205205
try {
206206
// 1. call transform method of defining transformers
207-
for (RegisteredTransformersRecord transformerRecord : new ArrayList<RegisteredTransformersRecord>(otherTransformers.values())) {
207+
for (RegisteredTransformersRecord transformerRecord : new ArrayList<>(otherTransformers.values())) {
208208
if ((className != null && transformerRecord.pattern.matcher(className).matches()) ||
209209
(redefiningClass != null && transformerRecord.pattern.matcher(redefiningClass.getName()).matches())) {
210210
for (ClassFileTransformer transformer : new ArrayList<ClassFileTransformer>(transformerRecord.transformerList)) {
@@ -221,7 +221,7 @@ public byte[] transform(final ClassLoader classLoader, String className, Class<?
221221
}
222222
// 2. call transform method of redefining transformers
223223
if (redefiningClass != null && className != null) {
224-
for (RegisteredTransformersRecord transformerRecord : new ArrayList<RegisteredTransformersRecord>(redefinitionTransformers.values())) {
224+
for (RegisteredTransformersRecord transformerRecord : new ArrayList<>(redefinitionTransformers.values())) {
225225
if (transformerRecord.pattern.matcher(className).matches()) {
226226
for (ClassFileTransformer transformer : new ArrayList<ClassFileTransformer>(transformerRecord.transformerList)) {
227227
if(transformer instanceof PluginClassFileTransformer) {
@@ -308,11 +308,11 @@ LinkedList<PluginClassFileTransformer> reduce(final ClassLoader classLoader, Lis
308308
}
309309
/**
310310
* Every classloader should be initialized. Usually if anything interesting happens,
311-
* it is initialized during plugin initialization process. However, some plugins (e.g. Hotswapper)
311+
* it is initialized during the plugin initialization process. However, some plugins (e.g. Hotswapper)
312312
* are triggered during classloader initialization process itself (@Init on static method). In this case,
313313
* the plugin will be never invoked, until the classloader initialization is invoked from here.
314314
*
315-
* Schedule with some timeout to allow standard plugin initialization process to precede.
315+
* Schedule with some timeout to allow a standard plugin initialization process to precede.
316316
*
317317
* @param classLoader the classloader to which this transformation is associated
318318
* @param protectionDomain associated protection domain (if any)
@@ -322,7 +322,13 @@ protected boolean ensureClassLoaderInitialized(final ClassLoader classLoader, fi
322322

323323
if (classLoader == null) {
324324
// directly init null (bootstrap) classloader
325-
PluginManager.getInstance().initClassLoader(null, protectionDomain);
325+
if (PluginManager.getInstance().getInitClassLoaderLock().tryLock()) {
326+
try {
327+
PluginManager.getInstance().initClassLoader(null, protectionDomain);
328+
} finally {
329+
PluginManager.getInstance().getInitClassLoaderLock().unlock();
330+
}
331+
}
326332
} else {
327333
// ensure the classloader should not be excluded
328334
if (shouldScheduleClassLoader(classLoader)) {
@@ -364,10 +370,13 @@ private boolean shouldScheduleClassLoader(final ClassLoader classLoader) {
364370

365371

366372
/**
367-
* Transform type to ^regexp$ form - match only whole pattern.
373+
* Normalizes a given regular expression for matching types by ensuring
374+
* it starts with a caret (^) and ends with a dollar sign ($).
368375
*
369-
* @param registeredType type
370-
* @return
376+
* @param registeredType the input regular expression for a type which may or may not
377+
* include starting or ending anchors.
378+
* @return a string representing the normalized regular expression, guaranteed
379+
* to start with ^ and end with $.
371380
*/
372381
protected String normalizeTypeRegexp(String registeredType) {
373382
String regexp = registeredType;

0 commit comments

Comments
 (0)