patterntesting.runtime.monitor.ClasspathMonitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of patterntesting-rt Show documentation
Show all versions of patterntesting-rt Show documentation
PatternTesting Runtime (patterntesting-rt) is the runtime component for
the PatternTesting framework. It provides the annotations and base classes
for the PatternTesting testing framework (e.g. patterntesting-check,
patterntesting-concurrent or patterntesting-exception) but can be also
used standalone for classpath monitoring or profiling.
It uses AOP and AspectJ to perform this feat.
/**
* $Id: ClasspathMonitor.java,v 1.31 2014/01/04 21:56:42 oboehm Exp $
*
* Copyright (c) 2008 by Oliver Boehm
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* (c)reated 10.02.2009 by oliver ([email protected])
*/
package patterntesting.runtime.monitor;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.jar.*;
import java.util.zip.*;
import javax.management.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.*;
//import patterntesting.runtime.annotation.ProfileMe;
import patterntesting.runtime.jmx.*;
import patterntesting.runtime.util.*;
/**
* To avoid classpath problems like double entries of the same class or resource
* in the classpath there are several methods available.
* To get the boot classpath the system property "sun.boot.class.path" is used to
* get them. This will work of course only for the SunVM.
*
* @author oliver
* @since 10.02.2009
* @version $Revision: 1.31 $
* @{link "http://www.javaworld.com/javaworld/javatips/jw-javatip105.html"}
*/
public final class ClasspathMonitor extends Thread implements ClasspathMonitorMBean {
private static final long serialVersionUID = 20090511L;
private static final Logger log = LoggerFactory.getLogger(ClasspathMonitor.class);
private static final Executor executor = Executors.newCachedThreadPool();
/** the classpath digger. */
private final transient ClasspathDigger classpathDigger;
private final transient ClassLoader cloader;
private final String[] classpath;
private String[] loadedClasses = new String[0];
private final transient FutureTask allClasspathClasses;
private final transient FutureTask> unusedClasses;
private final List> incompatibleClassList = new ArrayList>();
private static ClasspathMonitor instance;
private static boolean shutdownHook = false;
/** The doublet list (default visibility for testing). */
private List> doubletList;
static {
instance = new ClasspathMonitor();
}
/**
* Instantiates a new classpath monitor.
*/
private ClasspathMonitor() {
this.reset();
this.classpathDigger = new ClasspathDigger();
this.cloader = this.classpathDigger.getClassLoader();
this.classpath = this.classpathDigger.getClasspath();
this.allClasspathClasses = getFutureCasspathClasses();
this.unusedClasses = getFutureUnusedClasses();
}
private FutureTask getFutureCasspathClasses() {
Callable callable = new Callable() {
public String[] call() throws Exception {
return getClasspathClassArray();
}
};
FutureTask classes = new FutureTask(callable);
executor.execute(classes);
return classes;
}
private FutureTask> getFutureUnusedClasses() {
Callable> callable = new Callable>() {
public Set call() throws Exception {
return getClasspathClassSet();
}
};
FutureTask> classes = new FutureTask>(callable);
executor.execute(classes);
return classes;
}
/**
* Resets the doubletList and maybe later other things.
* At the moment it is only used by ClasspathMonitorTest
* by the testGetDoubletListPerformance() method.
*/
protected void reset() {
doubletList = new ArrayList>();
}
/**
* Yes, it is a Singleton because it offers only some services. So we don't
* need the object twice.
*
* @return the only instance
*/
public static ClasspathMonitor getInstance() {
return instance;
}
/**
* Register as m bean.
*/
public static void registerAsMBean() {
if (isRegisteredAsMBean()) {
log.debug("MBean already registered - registerAsMBean() ignored");
}
try {
MBeanHelper.registerMBean(getInstance());
} catch (JMException e) {
if (log.isInfoEnabled()) {
log.info(instance + " can't be registered as MBean (" + e
+ ")");
}
}
}
/**
* If you want to ask JMX if bean is already registered you can ask the
* MBeanHelper or you can ask this method.
*
* @since 1.0
* @return true if MBean is already registered.
*/
public static boolean isRegisteredAsMBean() {
return MBeanHelper.isRegistered(getInstance());
}
/**
* Which resource.
*
* @param name the name
*
* @return the URI
*
* @see ClasspathMonitorMBean#whichResource(String)
*/
//@ProfileMe
@Description("returns the URI of the given resource")
public URI whichResource(final String name) {
return classpathDigger.whichResource(name);
}
/**
* Which class.
*
* @param name the name
*
* @return the URI
*
* @see patterntesting.runtime.monitor.ClasspathMonitorMBean#whichClass(java.lang.String)
*/
//@ProfileMe
public URI whichClass(final String name) {
String resource = Converter.classToResource(name);
return whichResource(resource);
}
/**
* Where is the given class found in the classpath? Which class was loaded
* from which URI?.
*
* @param clazz the class
*
* @return file or jar path
*/
//@ProfileMe
public URI whichClass(final Class> clazz) {
return whichClass(clazz.getName());
}
/**
* Returns the jar file or path where the given classname was found.
*
* @param classname e.g. java.lang.String
*
* @return jar or path as URI
*/
public URI whichClassPath(final String classname) {
String resource = Converter.classToResource(classname);
return whichResourcePath(resource);
}
/**
* Returns the jar file or path where the given class was found.
*
* @param clazz e.g. Sting.class
*
* @return jar or path as URI
*/
public URI whichClassPath(final Class> clazz) {
return whichClassPath(clazz.getName());
}
/**
* Which class path.
*
* @param p the p
*
* @return the uRI
*/
public URI whichClassPath(final Package p) {
String resource = Converter.toResource(p);
return whichResourcePath(resource);
}
/**
* Returns the jar file or path where the given resource was found.
*
* @param resource e.g. log4j.properties
*
* @return jar or path as URI
*/
public URI whichResourcePath(final String resource) {
URI uri = this.whichResource(resource);
if (uri == null) {
return null;
}
return ClasspathHelper.getParent(uri, resource);
}
/**
* Which class jar.
*
* @param clazz the clazz
*
* @return the jar file
*/
public JarFile whichClassJar(final Class> clazz) {
return whichClassJar(clazz.getName());
}
/**
* Which class jar.
*
* @param classname the classname
*
* @return the jar file
*/
public JarFile whichClassJar(final String classname) {
String resource = Converter.classToResource(classname);
return whichResourceJar(resource);
}
/**
* Which resource jar.
*
* @param resource the resource
*
* @return the jar file
*/
public JarFile whichResourceJar(final String resource) {
return whichResourceJar(this.whichResourcePath(resource));
}
/**
* Which resource jar.
*
* @param resource the resource
*
* @return the jar file
*/
public static JarFile whichResourceJar(final URI resource) {
if (resource == null) {
return null;
}
File file = Converter.toFile(resource);
try {
return new JarFile(file);
} catch (IOException ioe) {
log.debug("can't read " + file, ioe);
return null;
}
}
/**
* Gets the resources.
*
* @param name the name
* @return the resources
*/
//@ProfileMe
public Enumeration getResources(final String name) {
try {
Enumeration resources = cloader.getResources(name);
if (!resources.hasMoreElements()) {
if (name.startsWith("/")) {
return getResources(name.substring(1));
}
if (log.isDebugEnabled()) {
log.debug(name + " not found in classpath");
}
}
return resources;
} catch (IOException ioe) {
log.info(name + " not found in classpath", ioe);
return null;
}
}
/**
* Gets the resource.
*
* @param name the name
*
* @return the resource
*/
public static URL getResource(final String name) {
return ClasspathMonitor.class.getResource(name);
}
/**
* @param name the name of the resource
* @return number of resources
* @see ClasspathMonitorMBean#getNoResources(java.lang.String)
*/
//@ProfileMe
public int getNoResources(final String name) {
Enumeration resources = getResources(name);
if (resources == null) {
return 0;
}
int n = 0;
while (resources.hasMoreElements()) {
n++;
resources.nextElement();
}
if (log.isTraceEnabled()) {
log.trace(n + " element(s) of " + name + " found in classpath");
}
return n;
}
/**
* Gets the no classes.
*
* @param cl the cl
*
* @return the no classes
*/
public int getNoClasses(final Class> cl) {
return getNoClasses(cl.getName());
}
/**
* @param classname the classname
* @return number of classes
* @see ClasspathMonitorMBean#getNoClasses(java.lang.String)
*/
public int getNoClasses(final String classname) {
return getNoResources(Converter.classToResource(classname));
}
/**
* Checks if is doublet.
*
* @param name the name
*
* @return true, if checks if is doublet
*
* @see patterntesting.runtime.monitor.ClasspathMonitorMBean#isDoublet(java.lang.String)
*/
//@ProfileMe
public boolean isDoublet(final String name) {
Enumeration resources = getResources(name);
if ((resources == null) || !resources.hasMoreElements()) {
throw new NoSuchElementException(name);
}
resources.nextElement();
if (resources.hasMoreElements()) {
logDoublets(name);
return true;
} else {
return false;
}
}
/**
* Is the given class a doublet, i.e. can it be found more than once in the
* classpath?
*
* @param clazz the class to be checked
* @return true if class is found more than once in the classpath
*/
//@ProfileMe
public boolean isDoublet(final Class> clazz) {
String classname = clazz.getName();
String resource = Converter.classToResource(classname);
return isDoublet(resource);
}
/**
* Gets the first doublet.
*
* @param name the name
*
* @return the first doublet
*
* @see patterntesting.runtime.monitor.ClasspathMonitorMBean#getFirstDoublet(java.lang.String)
*/
public URL getFirstDoublet(final String name) {
return getDoublet(name, 1);
}
/**
* Gets the first doublet.
*
* @param clazz the class
* @return the first doublet
*/
public URL getFirstDoublet(final Class> clazz) {
return getDoublet(clazz, 1);
}
/**
* Gets the doublet.
*
* @param name the name
* @param nr the nr
*
* @return the doublet
*
* @see patterntesting.runtime.monitor.ClasspathMonitorMBean#getDoublet(java.lang.String,
* int)
*/
public URL getDoublet(final String name, final int nr) {
Enumeration resources = getResources(name);
for (int i = 0; resources.hasMoreElements(); i++) {
URL url = resources.nextElement();
if (i == nr) {
return url;
}
}
return null;
}
/**
* Gets the doublet.
*
* @param clazz the clazz
* @param nr the nr
*
* @return the doublet
*/
public URL getDoublet(final Class> clazz, final int nr) {
String resource = Converter.classToResource(clazz.getName());
return getDoublet(resource, nr);
}
/**
* Log doublets.
*
* @param name the name
*/
private void logDoublets(final String name) {
if (log.isTraceEnabled()) {
List doublets = new Vector();
Enumeration resources = getResources(name);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
doublets.add(url);
}
log.trace(name + " doublets found: " + doublets);
}
}
/**
* Gets the doublet list.
*
* @return the doublet list
*/
//@ProfileMe
public synchronized List> getDoubletList() {
if (multiThreadingEnabled) {
return getDoubletListParallel();
} else {
return getDoubletListSerial();
}
}
/**
* Looks for each loaded class if it is a doublet or not. For the
* performance reason it looks in the doubletList from the last time if it
* is already found. This is done because normally the number of doublets
* does not decrease.
*
* @return a sorted list of found doublets
*/
//@ProfileMe
protected synchronized List> getDoubletListSerial() {
List> loadedClassList = this.getLoadedClassList();
for (Class> clazz : loadedClassList) {
// TODO this can be parallized
if (doubletList.contains(clazz)) {
continue;
}
try {
if (this.isDoublet(clazz)) {
doubletList.add(clazz);
}
} catch (NoSuchElementException nsee) {
if (log.isTraceEnabled()) {
log.trace(clazz + " not found -> ignored");
}
}
}
Collections.sort(doubletList, new ObjectComparator());
return doubletList;
}
/**
* Gets the doublets.
*
* @return the doublets
*
* @see ClasspathMonitorMBean#getDoublets()
*/
//@ProfileMe
public String[] getDoublets() {
log.debug("calculating doublets...");
List> classes = this.getDoubletList();
String[] doublets = new String[classes.size()];
for (int i = 0; i < doublets.length; i++) {
doublets[i] = classes.get(i).toString();
}
return doublets;
}
/**
* Gets the doublet classpath.
*
* @return the doublet classpath
*
* @see ClasspathMonitorMBean#getDoubletClasspath()
*/
//@ProfileMe
public String[] getDoubletClasspath() {
try {
log.debug("calculating doublet-classpath...");
Set classpathSet = getClasspathSet(this.getDoubletList());
return getAsArray(classpathSet);
} catch (ConcurrentModificationException e) {
log.info(e + " happens while sorting classes - will try it again...");
ThreadUtil.sleep();
return getDoubletClasspath();
}
}
/**
* Gets the classpath set.
*
* @param classes the classes
*
* @return the classpath set
*/
private SortedSet getClasspathSet(final List> classes) {
SortedSet clpath = new TreeSet();
for (Class> cl : classes) {
String resource = Converter.toResource(cl);
Enumeration resources = this.getResources(resource);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
URI path = Converter.toURI(url);
clpath.add(ClasspathHelper.getParent(path, resource));
}
}
return clpath;
}
/**
* Dumps the internal fields of the classloader (after an idea from
* "Java ist auch eine Insel").
*
* {@link "http://www.galileocomputing.de/openbook/javainsel6/javainsel_21_003.htm#mj7e2d89f4b96fde1900bc09fd1db83fb1"}
*
* @return the class loader details
*/
//@ProfileMe
public String getClassLoaderDetails() {
StringBuffer sbuf = new StringBuffer("dump of " + cloader + ":\n");
for (Class> cl = this.cloader.getClass(); cl != null; cl = cl
.getSuperclass()) {
sbuf.append('\t');
dumpFields(sbuf, cl, this.cloader);
}
return sbuf.toString().trim();
}
/**
* Dump fields.
*
* @param sbuf the sbuf
* @param cl the cl
* @param obj the obj
*/
//@ProfileMe
private static void dumpFields(final StringBuffer sbuf, final Class> cl,
final Object obj) {
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (int i = 0; i < fields.length; i++) {
sbuf.append(fields[i]);
sbuf.append(" = ");
try {
sbuf.append(fields[i].get(obj));
} catch (Exception e) {
sbuf.append("<" + e + ">");
}
sbuf.append('\n');
}
}
/**
* Checks if is classloader supported.
*
* @return true if it is a known classloader
*/
public boolean isClassloaderSupported() {
return this.classpathDigger.isClassloaderSupported();
}
/**
* Returns some information about the classloader. At least the user should
* be informed if it is a unknown classloader which is not supported or not
* tested.
*
* @return e.g. "unknown classloader xxx - classpath can be wrong"
*/
public String getClassloaderInfo() {
String info = this.isClassloaderSupported() ? "supported" : "unsupported";
return this.cloader.getClass().getName() + " (" + info + ")";
}
/**
* Gets the loaded packages.
*
* @return the loaded packages
*
* @see ClasspathMonitorMBean#getLoadedPackages()
*/
public String[] getLoadedPackages() {
Package[] packages = this.classpathDigger.getLoadedPackageArray();
String[] strings = new String[packages.length];
for (int i = 0; i < packages.length; i++) {
strings[i] = packages[i].toString();
}
Arrays.sort(strings);
return strings;
}
/**
* This method is used by PatternTesting Samples (in packages.jsp). So it
* is left here as wrapper around ClasspathDigger.
*
* @return array of packages
*/
public Package[] getLoadedPackageArray() {
return this.classpathDigger.getLoadedPackageArray();
}
/**
* If you want to dump all packages you can use this method. The output will
* be sorted.
*
* @return each package in a single line
*/
public String getLoadedPackagesAsString() {
String[] packages = getLoadedPackages();
StringBuffer sbuf = new StringBuffer();
for (int i = 0; i < packages.length; i++) {
sbuf.append(packages[i]);
sbuf.append('\n');
}
return sbuf.toString().trim();
}
/**
* Returns a list of classes which were loaded by the classloader.
*
* @return list of classes
*/
//@ProfileMe
public synchronized List> getLoadedClassList() {
List> classlist = classpathDigger.getLoadedClassList();
return classlist;
}
/**
* As MBean a string array could be displayed by the 'jconsole'. A class
* array not.
*
* @return the classes as sorted string array
*/
public synchronized String[] getLoadedClasses() {
List> classes = getLoadedClassList();
if (classes.size() != loadedClasses.length) {
loadedClasses = new String[classes.size()];
for (int i = 0; i < loadedClasses.length; i++) {
loadedClasses[i] = classes.get(i).toString();
}
Arrays.sort(loadedClasses);
}
return loadedClasses;
}
/**
* Gets the loaded classes as string.
*
* @return the loaded classes as string
*/
//@ProfileMe
public String getLoadedClassesAsString() {
List> classes = getLoadedClassList();
Collections.sort(classes, new ObjectComparator());
StringBuffer sbuf = new StringBuffer();
for (Iterator> i = classes.iterator(); i.hasNext();) {
sbuf.append(i.next().toString().trim());
sbuf.append('\n');
}
return sbuf.toString().trim();
}
/**
* Checks if the given classname is loaded.
* Why does we use not Class as parameter here? If you would allow a
* parameter of type "Class" this class will be problably loaded before
* and this method will return always true!
*
* @param classname name of the class
* @return true if class is loaded
* @see patterntesting.runtime.monitor.ClasspathMonitorMBean#isLoaded(java.lang.String)
*/
public boolean isLoaded(final String classname) {
return this.classpathDigger.isLoaded(classname);
}
/**
* Unused classes are classes which are not loaded but which are found in
* the classpath.
*
* @return unused classes
*
* @see #getLoadedClasses()
* @see #getClasspathClasses()
*/
//@ProfileMe
public String[] getUnusedClasses() {
List> classes = getLoadedClassList();
Collection used = new ArrayList();
Set unusedSet = this.getUnusedClassSet();
for (Class> cl : classes) {
String classname = cl.getName();
if (unusedSet.contains(classname)) {
used.add(classname);
}
}
unusedSet.removeAll(used);
String[] unused = new String[unusedSet.size()];
unusedSet.toArray(unused);
return unused;
}
private Set getUnusedClassSet() {
// return this.unusedClassSet;
try {
return this.unusedClasses.get();
} catch (InterruptedException e) {
log.warn("no result from " + this.unusedClasses, e);
return this.getClasspathClassSet();
} catch (ExecutionException e) {
log.warn("no result from " + this.unusedClasses, e);
return this.getClasspathClassSet();
}
}
/**
* Scans the classpath for all classes.
* For performance reason we return no longer a copy but the origin array.
* Don't do changes for the returned array!
*
* @return all classes as String array
*/
public String[] getClasspathClasses() {
try {
return this.allClasspathClasses.get();
} catch (InterruptedException e) {
log.warn("no result from " + this.allClasspathClasses, e);
return this.getClasspathClassArray();
} catch (ExecutionException e) {
log.warn("no result from " + this.allClasspathClasses, e);
return this.getClasspathClassArray();
}
}
private Set getClasspathClassSet() {
String[] classes = getClasspathClasses();
return new TreeSet(Arrays.asList(classes));
}
/**
* Gets the classes of a given package name as collection.
*
* @param packageName the package name
* @return the classpath class list
*/
public Collection getClasspathClassList(final String packageName) {
Collection classlist = new ArrayList();
String[] classes = this.getClasspathClasses();
for (int i = 0; i < classes.length; i++) {
if (classes[i].startsWith(packageName)) {
classlist.add(classes[i]);
}
}
return classlist;
}
/**
* Gets the classes of the given type.
*
* @param the generic type
* @param packageName the package name
* @param type the type
* @return the classes
*/
@SuppressWarnings("unchecked")
public Collection> getClassList(final String packageName, final Class type) {
Collection> classes = new ArrayList>();
Collection> concreteClasses = getConcreteClassList(packageName);
for (Class> clazz : concreteClasses) {
if (type.isAssignableFrom(clazz)) {
classes.add(((Class) clazz));
log.trace("subclass of {} found: {}", type, clazz);
}
}
return classes;
}
/**
* Gets a list of concrete classes. These are classes which can be
* instantiated, i.e. they are not abstract and have a default
* constructor.
*
* @param packageName the package name
* @return the concrete class list
*/
public Collection> getConcreteClassList(final String packageName) {
assert packageName != null;
Collection classList = this.getClasspathClassList(packageName);
Collection> classes = new ArrayList>(classList.size());
for (String classname : classList) {
try {
Class> clazz = Class.forName(classname);
if (!canBeInstantiated(clazz)) {
if (log.isTraceEnabled()) {
log.trace(clazz + " will be ignored (can't be instantiated)");
}
continue;
}
classes.add(clazz);
} catch (ClassNotFoundException e) {
log.info(classname + " will be ignored (" + e.getMessage() + ")");
}
}
return classes;
}
// /**
// * Gets a list of concrete classes of the given type.
// * These are classes which can be instantiated, i.e. they are not abstract
// * and have a default constructor.
// *
// * @param packageName the package name
// * @param type the type
// * @return the classes
// */
// public Collection> getConcreteClassList(final String packageName, Class> type) {
// Collection> classes = new ArrayList>();
// Collection> concreteClasses = this.getConcreteClassList(packageName);
// for (Class> clazz : concreteClasses) {
// if (type.isAssignableFrom(clazz)) {
// classes.add(clazz);
// if (log.isTraceEnabled()) {
// log.trace(type + " found: " + clazz);
// }
// }
// }
// return classes;
// }
private static boolean canBeInstantiated(final Class> clazz) {
if (clazz.isInterface()) {
return false;
}
int mod = clazz.getModifiers();
if (Modifier.isAbstract(mod)) {
return false;
}
try {
clazz.getConstructor();
return true;
} catch (SecurityException e) {
log.info("can't get default ctor of " + clazz, e);
return false;
} catch (NoSuchMethodException e) {
return false;
}
}
/**
* Gets the classpath class set (sorted).
*
* @return the classpath class set
*/
//@ProfileMe
private Set createClasspathClassSet() {
Set classSet = new TreeSet();
for (int i = 0; i < classpath.length; i++) {
addClasses(classSet, new File(classpath[i]));
}
return classSet;
}
private String[] getClasspathClassArray() {
Set classSet = createClasspathClassSet();
return classSet.toArray(new String[classSet.size()]);
}
/**
* Adds the classes.
*
* @param classSet the class set
* @param path the path
*/
private static void addClasses(final Set classSet, final File path) {
if (log.isTraceEnabled()) {
log.trace("adding classes from " + path.getAbsolutePath() + "...");
}
try {
if (path.isDirectory()) {
addClassesFromDir(classSet, path);
} else {
addClassesFromArchive(classSet, path);
}
} catch (IOException e) {
log.warn("can't add classes from " + path.getAbsolutePath());
}
}
/**
* Adds the classes from dir.
*
* @param classSet the class set
* @param dir the dir
*
* @throws IOException Signals that an I/O exception has occurred.
*/
//@ProfileMe
private static void addClassesFromDir(final Set classSet, final File dir)
throws IOException {
ClassWalker classWalker = new ClassWalker(dir);
Collection classes = classWalker.getClasses();
classSet.addAll(classes);
}
/**
* Adds the classes from archive.
*
* @param classSet the class set
* @param archive the archive
*
* @throws IOException Signals that an I/O exception has occurred.
*/
//@ProfileMe
private static void addClassesFromArchive(final Set classSet, final File archive)
throws IOException {
ZipFile zipFile = new ZipFile(archive);
try {
Enumeration extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(".class")) {
classSet.add(Converter.resourceToClass(name));
}
}
} finally {
zipFile.close();
}
}
/**
* Gets the loaded classpath (without the bootclasspath) as sorted set.
* TODO: this method lets room for more performance (e.g. using a
* Hashtable for caching)
*
* @return the loaded classpath (excluding the bootclasspath)
*/
//@ProfileMe
public SortedSet getUsedClasspathSet() {
List> loadedClassList = this.getLoadedClassList();
SortedSet usedClasspathSet = new TreeSet();
for (Class> clazz : loadedClassList) {
URI classUri = this.whichClass(clazz);
if (classUri != null) {
URI classpathURI = ClasspathHelper.getParent(classUri, clazz);
usedClasspathSet.add(classpathURI);
}
}
return usedClasspathSet;
}
/**
* It might be that the used classpath changes during the calculation of it.
* So it is only a good approximation how it looks at the moment of the
* call.
*
* @return the used classpath
*
* @see ClasspathMonitor#getUsedClasspath()
*/
//@ProfileMe
public String[] getUsedClasspath() {
log.debug("calculating used classpath...");
SortedSet classpathSet = this.getUsedClasspathSet();
return getAsArray(classpathSet);
}
/**
* Gets the as array.
*
* @param classpathSet the classpath set
*
* @return the as array
*/
private static String[] getAsArray(final Set classpathSet) {
String[] classpath = new String[classpathSet.size()];
Object[] a = classpathSet.toArray();
for (int i = 0; i < classpath.length; i++) {
URI uri = (URI) a[i];
classpath[i] = uri.getPath();
if (StringUtils.isEmpty(classpath[i])) {
classpath[i] = StringUtils.substringAfterLast(uri.toString(),
":");
}
if (classpath[i].endsWith(File.separator)) {
classpath[i] = classpath[i].substring(0,
(classpath[i].length() - 1));
}
}
Arrays.sort(classpath);
return classpath;
}
/**
* The unused classpath is this path which are not used in past.
*
* @return the unused classpath (sorted)
*/
//@ProfileMe
public String[] getUnusedClasspath() {
log.debug("calculating unused classpath...");
SortedSet unused = new TreeSet();
log.trace(Arrays.class + " loaded (to get corrected used classpath");
String[] used = this.getUsedClasspath();
for (int i = 0; i < classpath.length; i++) {
String path = classpath[i];
if (Arrays.binarySearch(used, path) < 0) {
unused.add(path);
}
}
String[] a = new String[unused.size()];
return unused.toArray(a);
}
/**
* To get the boot classpath the sytem property "sun.boot.class.path" is
* used to get them. This will work of course only for the SunVM.
*
* @return the boot classpath as String array
*/
public String[] getBootClasspath() {
return this.classpathDigger.getBootClasspath();
}
/**
* The classpath is stored in the system property "java.class.path". And
* this is the classpath which will be returned.
*
* @return the classpath as String array
*/
public String[] getClasspath() {
return this.classpath;
//return Arrays.copyOf(this.classpath, this.classpath.length);
}
/**
* @param classname
* name of the class
* @return the serialVersionUID
* @throws IllegalAccessException
* if class can't be accessed
* @see patterntesting.runtime.monitor.ClasspathMonitorMBean#getSerialVersionUID(java.lang.String)
*/
public Long getSerialVersionUID(final String classname)
throws IllegalAccessException {
try {
Class> clazz = Class.forName(classname);
return getSerialVersionUID(clazz);
} catch (ClassNotFoundException e) {
log.info(classname + " not found (" + e.getLocalizedMessage()
+ ")");
return null;
}
}
/**
* Gets the serial version uid.
*
* @param clazz the clazz
*
* @return the serial version uid
*
* @throws IllegalAccessException the illegal access exception
*/
public Long getSerialVersionUID(final Class> clazz)
throws IllegalAccessException {
try {
Field field = ReflectionHelper.getField(clazz, "serialVersionUID");
return (Long) field.get(null);
} catch (NoSuchFieldException e) {
log.debug(clazz + " has no serialVersionUID");
return null;
}
}
/**
* If a MANIFEST is found for a given class the attributes in this
* file should be returned as a string array.
* E.g. for commons-lang-2.3.jar the string array may looks like
*
* Manifest-Version: 1.0
* Ant-Version: Apache Ant 1.6.5
* Created-By: 1.3.1_09-85 ("Apple Computer, Inc.")
* Package: org.apache.commons.lang
* Extension-Name: commons-lang
* Specification-Version: 2.3
* Specification-Vendor: Apache Software Foundation
* Specification-Title: Commons Lang
* Implementation-Version: 2.3
* Implementation-Vendor: Apache Software Foundation
* Implementation-Title: Commons Lang
* Implementation-Vendor-Id: org.apache
* X-Compile-Source-JDK: 1.3
* X-Compile-Target-JDK: 1.1
*
*
* @param clazz the clazz
*
* @return the attribute entries of the Manifest
* (or emtpy array if no Manifest or no attributes are found)
*/
public String[] getManifestEntries(final Class> clazz) {
return this.getManifestEntries(clazz.getName());
}
/**
* If a MANIFEST is found for a given class the attributes in this
* file should be returned as a string array.
* E.g. for commons-lang-2.3.jar the string array may looks like
*
* Manifest-Version: 1.0
* Ant-Version: Apache Ant 1.6.5
* Created-By: 1.3.1_09-85 ("Apple Computer, Inc.")
* Package: org.apache.commons.lang
* Extension-Name: commons-lang
* Specification-Version: 2.3
* Specification-Vendor: Apache Software Foundation
* Specification-Title: Commons Lang
* Implementation-Version: 2.3
* Implementation-Vendor: Apache Software Foundation
* Implementation-Title: Commons Lang
* Implementation-Vendor-Id: org.apache
* X-Compile-Source-JDK: 1.3
* X-Compile-Target-JDK: 1.1
*
*
* @param classname (must be in the classpath, otherwise you'll get a
* IllegalArgumentException)
*
* @return the attribute entries of the Manifest
* (or emtpy array if no Manifest or no attributes are found)
*/
public String[] getManifestEntries(final String classname) {
URI classpathURI = whichClassPath(classname);
if (classpathURI == null) {
throw new IllegalArgumentException(classname + " not found in classpath");
}
File path = Converter.toFile(classpathURI);
return getManifestEntries(path);
}
/**
* Gets the manifest entries.
*
* @param path e.g. a classpath or a JAR file
*
* @return the attribute entries of the manifest file
*/
private String[] getManifestEntries(final File path) {
if (path.isFile()) {
try {
return getManifestEntries(new JarFile(path));
} catch (IOException ioe) {
log.info("no manifest found in " + path, ioe);
return new String[0];
}
}
File manifestFile = new File(path, "META-INF/MANIFEST.MF");
if (!manifestFile.exists()) {
if (log.isDebugEnabled()) {
log.debug(manifestFile + " does not exist");
}
return new String[0];
}
try {
InputStream istream = new FileInputStream(manifestFile);
Manifest manifest = new Manifest(istream);
IOUtils.closeQuietly(istream);
return getManifestEntries(manifest);
} catch (IOException ioe) {
log.info("can't read " + manifestFile, ioe);
return new String[0];
}
}
/**
* We look for the manifest file for the given JAR file.
*
* @param jarfile the jarfile
*
* @return the entries as string array (may be an empty array if no
* manifest file was found)
*/
private String[] getManifestEntries(final JarFile jarfile) {
Manifest manifest;
try {
manifest = jarfile.getManifest();
} catch (IOException ioe) {
log.info("no manifest found in " + jarfile, ioe);
return new String[0];
}
if (manifest == null) {
if (log.isDebugEnabled()) {
log.debug("no manifest found in " + jarfile);
}
return new String[0];
}
return getManifestEntries(manifest);
}
/**
* Gets the manifest entries.
*
* @param manifest the manifest
*
* @return the manifest entries
*/
private String[] getManifestEntries(final Manifest manifest) {
Attributes attributes = manifest.getMainAttributes();
String[] manifestEntries = new String[attributes.size()];
Set> entries = attributes.entrySet();
Iterator> iterator = entries.iterator();
for (int i = 0; i < manifestEntries.length; i++) {
Map.Entry