Skip to content

Commit db5ca43

Browse files
committed
Readd "discovery" of liverepl to support SBT
1 parent 989e7c7 commit db5ca43

File tree

10 files changed

+378
-0
lines changed

10 files changed

+378
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package net.djpowell.liverepl.discovery;
2+
3+
import java.util.Collection;
4+
5+
/**
6+
* SPI Interface for providing implementations to discover ClassLoaders.
7+
*/
8+
public interface ClassLoaderDiscovery {
9+
/**
10+
* Return information about the available ClassLoaders.
11+
* Implementations must register each ClassLoader with the application's ClassLoaderRegistry,
12+
* and use the id returned by the registry in the ClassLoaderInfo instances returned.
13+
*/
14+
Collection<ClassLoaderInfo> listClassLoaders();
15+
String discoveryName();
16+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package net.djpowell.liverepl.discovery;
2+
3+
import java.lang.ref.WeakReference;
4+
5+
public class ClassLoaderInfo implements Comparable<Object> {
6+
7+
public final String id;
8+
private final WeakReference<ClassLoader> classLoader;
9+
public final String info;
10+
11+
public ClassLoaderInfo(String id, ClassLoader classLoader, String info) {
12+
this.id = id;
13+
this.classLoader = new WeakReference<ClassLoader>(classLoader);
14+
this.info = info;
15+
}
16+
17+
public ClassLoader getClassLoader() {
18+
return classLoader.get();
19+
}
20+
21+
public String getClassLoaderName() {
22+
ClassLoader cl = classLoader.get();
23+
if (cl == null) {
24+
return "<null>";
25+
} else {
26+
return cl.getClass().getSimpleName();
27+
}
28+
}
29+
30+
public static final String header = String.format("#%-3s %-20s %s", "Id", "ClassLoader", "Info");
31+
32+
public String toString() {
33+
return String.format("%-3s %-20s %s", id, getClassLoaderName(), info);
34+
}
35+
36+
public int compareTo(Object o) {
37+
ClassLoaderInfo cli = (ClassLoaderInfo)o;
38+
return id.compareTo(cli.id);
39+
}
40+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package net.djpowell.liverepl.discovery;
2+
3+
import java.util.Map;
4+
import java.util.WeakHashMap;
5+
import java.util.concurrent.atomic.AtomicInteger;
6+
7+
/**
8+
* ClassLoaderDiscovery implementations must register all ClassLoaders with this registry to give them an
9+
* identifier which can be used to subsequently find the ClassLoader that they user asked for.
10+
*
11+
*/
12+
public class ClassLoaderRegistry {
13+
14+
private final AtomicInteger clIdGenerator = new AtomicInteger(0);
15+
private final Map<ClassLoader, String> clIdMap = new WeakHashMap<ClassLoader, String>();
16+
17+
public String registerClassLoader(ClassLoader classLoader) {
18+
String id;
19+
synchronized (clIdMap) {
20+
id = clIdMap.get(classLoader);
21+
if (id == null) {
22+
id = String.valueOf(clIdGenerator.getAndIncrement());
23+
clIdMap.put(classLoader, id);
24+
}
25+
}
26+
return id;
27+
}
28+
29+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package net.djpowell.liverepl.discovery;
2+
3+
import net.djpowell.liverepl.discovery.impl.SystemDiscovery;
4+
import net.djpowell.liverepl.discovery.impl.ThreadDiscovery;
5+
import net.djpowell.liverepl.discovery.impl.TomcatDiscovery;
6+
7+
import java.io.PrintStream;
8+
import java.util.ArrayList;
9+
import java.util.Collection;
10+
import java.util.List;
11+
12+
/**
13+
* API for obtaining a list of ClassLoaders by using available implementations of ClassLoaderDiscovery
14+
*/
15+
public class Discovery implements ClassLoaderDiscovery {
16+
17+
private final List<ClassLoaderDiscovery> impls = new ArrayList<ClassLoaderDiscovery>();
18+
19+
public Discovery() {
20+
ClassLoaderRegistry registry=new ClassLoaderRegistry();
21+
impls.add(new SystemDiscovery(registry));
22+
impls.add(new TomcatDiscovery(registry));
23+
impls.add(new ThreadDiscovery(registry));
24+
}
25+
26+
public Collection<ClassLoaderInfo> listClassLoaders() {
27+
List<ClassLoaderInfo> ret = new ArrayList<ClassLoaderInfo>();
28+
for (ClassLoaderDiscovery discovery : impls) {
29+
ret.addAll(discovery.listClassLoaders());
30+
}
31+
return ret;
32+
}
33+
34+
public String discoveryName() {
35+
return "All ClassLoaders";
36+
}
37+
38+
public void dumpList(PrintStream out) {
39+
for (ClassLoaderDiscovery discovery : impls) {
40+
Collection<ClassLoaderInfo> clis = discovery.listClassLoaders();
41+
if (!clis.isEmpty()) {
42+
out.println();
43+
out.println(discovery.discoveryName() + ":");
44+
out.println();
45+
out.println(ClassLoaderInfo.header);
46+
for (ClassLoaderInfo cli : clis) {
47+
out.println(cli.toString());
48+
}
49+
}
50+
}
51+
}
52+
53+
public ClassLoaderInfo findClassLoader(String clId) {
54+
for (ClassLoaderInfo cli : listClassLoaders()) {
55+
if (cli.id.equals(clId)) {
56+
return cli;
57+
}
58+
}
59+
return null;
60+
}
61+
62+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package net.djpowell.liverepl.discovery;
2+
3+
public interface Function<R, A> {
4+
5+
R invoke (A arg);
6+
7+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
https://github.com/djpowell/liverepl
2+
3+
Copyright (c) 2009 David Powell
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
22+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package net.djpowell.liverepl.discovery.impl;
2+
3+
import net.djpowell.liverepl.discovery.ClassLoaderDiscovery;
4+
import net.djpowell.liverepl.discovery.ClassLoaderInfo;
5+
import net.djpowell.liverepl.discovery.ClassLoaderRegistry;
6+
import net.djpowell.liverepl.discovery.Function;
7+
8+
import javax.management.*;
9+
import java.lang.management.ManagementFactory;
10+
import java.util.Collection;
11+
import java.util.Set;
12+
import java.util.TreeSet;
13+
14+
/**
15+
* Helper class to return ClassLoaders using JMX
16+
*/
17+
public class JMXDiscovery implements ClassLoaderDiscovery {
18+
19+
private final ClassLoaderRegistry registry;
20+
private final MBeanServer mbs;
21+
private final String searchName;
22+
private final String attributeName;
23+
private final Function<ClassLoader, Object> getClassLoader;
24+
private final Function<String, ObjectName> getInfo;
25+
private final String discoveryName;
26+
27+
public JMXDiscovery(ClassLoaderRegistry registry, String searchName, String attributeName, Function<ClassLoader, Object> getClassLoader, Function<String, ObjectName> getInfo, String discoveryName) {
28+
this.registry = registry;
29+
this.mbs = ManagementFactory.getPlatformMBeanServer();
30+
this.searchName = searchName;
31+
this.attributeName = attributeName;
32+
this.getClassLoader = getClassLoader;
33+
this.getInfo = getInfo;
34+
this.discoveryName = discoveryName;
35+
}
36+
37+
public Collection<ClassLoaderInfo> listClassLoaders() {
38+
Set<ClassLoaderInfo> ret = new TreeSet<ClassLoaderInfo>();
39+
Set<ObjectName> names;
40+
try {
41+
names = mbs.queryNames(new ObjectName(searchName), null);
42+
} catch (MalformedObjectNameException e) {
43+
throw new RuntimeException(e);
44+
}
45+
46+
for (ObjectName name : names) {
47+
Object obj;
48+
try {
49+
obj = mbs.getAttribute(name, attributeName);
50+
} catch (RuntimeException e) {
51+
throw e;
52+
} catch (Exception e) {
53+
throw new RuntimeException(e);
54+
}
55+
ClassLoader classLoader = getClassLoader.invoke(obj);
56+
String id = registry.registerClassLoader(classLoader);
57+
String info = getInfo.invoke(name);
58+
ClassLoaderInfo cli = new ClassLoaderInfo(id, classLoader, info);
59+
ret.add(cli);
60+
}
61+
return ret;
62+
}
63+
64+
public String discoveryName() {
65+
return discoveryName;
66+
}
67+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.djpowell.liverepl.discovery.impl;
2+
3+
import net.djpowell.liverepl.discovery.ClassLoaderDiscovery;
4+
import net.djpowell.liverepl.discovery.ClassLoaderInfo;
5+
import net.djpowell.liverepl.discovery.ClassLoaderRegistry;
6+
7+
import java.util.Collection;
8+
import java.util.Collections;
9+
10+
/**
11+
* Simple implementation of ClassLoaderDiscovery for returning the SystemClassLoader.
12+
* This implementation should be registered first with Discovery to ensure that the
13+
* well-known SystemClassLoader gets assigned 0 as its id.
14+
*/
15+
public class SystemDiscovery implements ClassLoaderDiscovery {
16+
17+
private final String id;
18+
19+
public SystemDiscovery(ClassLoaderRegistry registry) {
20+
id = registry.registerClassLoader(ClassLoader.getSystemClassLoader());
21+
}
22+
23+
public Collection<ClassLoaderInfo> listClassLoaders() {
24+
ClassLoaderInfo cli = new ClassLoaderInfo(id, ClassLoader.getSystemClassLoader(), "<system>");
25+
return Collections.singletonList(cli);
26+
}
27+
28+
public String discoveryName() {
29+
return "System Class Loader";
30+
}
31+
32+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package net.djpowell.liverepl.discovery.impl;
2+
3+
import net.djpowell.liverepl.discovery.ClassLoaderDiscovery;
4+
import net.djpowell.liverepl.discovery.ClassLoaderInfo;
5+
import net.djpowell.liverepl.discovery.ClassLoaderRegistry;
6+
7+
import java.util.*;
8+
9+
/**
10+
* Implementation of ClassLoaderDiscovery that makes available the Thread Context ClassLoaders
11+
* associated with currently running threads.
12+
*/
13+
public class ThreadDiscovery implements ClassLoaderDiscovery {
14+
15+
private final ClassLoaderRegistry registry;
16+
17+
public ThreadDiscovery(ClassLoaderRegistry registry) {
18+
this.registry = registry;
19+
}
20+
21+
public Collection<ClassLoaderInfo> listClassLoaders() {
22+
Collection<ClassLoaderInfo> ret = new ArrayList<ClassLoaderInfo>();
23+
ClassLoader systemCl = ClassLoader.getSystemClassLoader();
24+
Collection<Thread> threads = new HashSet<Thread>(Thread.getAllStackTraces().keySet());
25+
Map<ClassLoader, String> classLoaders = new HashMap<ClassLoader, String>();
26+
for (Thread thread : threads) {
27+
ClassLoader classLoader = thread.getContextClassLoader();
28+
if (classLoader == null) continue;
29+
if (classLoader == systemCl) continue;
30+
classLoaders.put(classLoader, thread.getName() + " #" + thread.getId() + " [" + thread.getThreadGroup().getName() + "]");
31+
}
32+
TreeSet<Map.Entry<ClassLoader, String>> entries = new TreeSet<Map.Entry<ClassLoader, String>>(
33+
new Comparator<Map.Entry<ClassLoader, String>>() {
34+
public int compare(Map.Entry<ClassLoader, String> o1, Map.Entry<ClassLoader, String> o2) {
35+
return o1.getValue().compareTo(o2.getValue());
36+
}
37+
});
38+
entries.addAll(classLoaders.entrySet());
39+
for (Map.Entry<ClassLoader, String> entry : entries) {
40+
ClassLoader classLoader = entry.getKey();
41+
String threadName = entry.getValue();
42+
String id = registry.registerClassLoader(classLoader);
43+
ClassLoaderInfo cli = new ClassLoaderInfo(id, classLoader, threadName);
44+
ret.add(cli);
45+
}
46+
return ret;
47+
}
48+
49+
public String discoveryName() {
50+
return "Thread Context Class Loaders";
51+
}
52+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package net.djpowell.liverepl.discovery.impl;
2+
3+
import net.djpowell.liverepl.discovery.ClassLoaderDiscovery;
4+
import net.djpowell.liverepl.discovery.ClassLoaderInfo;
5+
import net.djpowell.liverepl.discovery.ClassLoaderRegistry;
6+
import net.djpowell.liverepl.discovery.Function;
7+
8+
import javax.management.ObjectName;
9+
import java.util.Collection;
10+
11+
/**
12+
* Implementation of ClassLoaderDiscovery which uses JMX to obtain the ClassLoaders associated
13+
* with Tomcat web applications.
14+
*/
15+
public class TomcatDiscovery implements ClassLoaderDiscovery {
16+
17+
private final JMXDiscovery jmxDiscovery;
18+
19+
public TomcatDiscovery(ClassLoaderRegistry registry) {
20+
this.jmxDiscovery = new JMXDiscovery(registry, "Catalina:j2eeType=WebModule,*", "loader",
21+
new Function<ClassLoader, Object>() {
22+
public ClassLoader invoke(Object obj) {
23+
ClassLoader classLoader;
24+
try {
25+
classLoader = (ClassLoader) obj.getClass().getMethod("getClassLoader").invoke(obj);
26+
} catch (RuntimeException e) {
27+
throw e;
28+
} catch (Exception e) {
29+
throw new RuntimeException(e);
30+
}
31+
return classLoader;
32+
}
33+
},
34+
new Function<String, ObjectName>() {
35+
public String invoke(ObjectName arg) {
36+
String name = arg.getKeyProperty("name");
37+
int pos = name.lastIndexOf('/');
38+
if (pos != -1) name = name.substring(pos);
39+
return name;
40+
}
41+
}, "Tomcat Web Applications");
42+
}
43+
44+
public Collection<ClassLoaderInfo> listClassLoaders() {
45+
return jmxDiscovery.listClassLoaders();
46+
}
47+
48+
public String discoveryName() {
49+
return jmxDiscovery.discoveryName();
50+
}
51+
}

0 commit comments

Comments
 (0)