views:

336

answers:

4

I need to get names of all java packages loaded by the JVM. This is to display a package browser like the ones found in IDEs. I can get the package list of the current classloader and its ancestors by accessing protected "packages" field of the ClassLoader class. But i'm unable to get the packages loaded by other webapps as they have their own class loaders. I'm testing this on Weblogic server

A: 

You need to walk the tree of classloaders up by using getParent(), find all classes which extend ClassLoader, find all current instances (the debug API should help here). But that probably won't work for Web Servers because of the security policy (web apps are not allowed to peek at each other).

For Tomcat, there is an option to log all classes as they are loaded. This slows down a server pretty much but it might be an option on the development server.

That said, I'm pretty curious why you would need that. The most simple solution would be to list all JAR files, which your app brings along, with jar tvf and strip the last part of the path (the class file).

Aaron Digulla
+1  A: 

The expected behavior of the Weblogic security model is that you would not have access to the other web applications' class loaders. This is not really something that you will be able to get around - see this article for more information.

stevedbrown
I'm also wondering what he's up to. Maybe he's trying to find a way to scan web apps of other people for security holes?
Aaron Digulla
He might be trying to figure out which libraries are being used by several webapps so he can factor those out into a shared area. Can't JMX managers find out this sort of information?
ThaDon
I'm really not sure - if they can, post it, you'll definitely have my vote.
stevedbrown
Yes Im trying to get library names used by all applications deployed on a server. This answer is correct as far as Im concerned since I couldnt find a way to get a reference to other webapps' class loaders. So I ended up scanning the deployed ear, war, jars and expanded folders. See my answer below.
maneesh
A: 

The only way I can think of doing this would be to modify each webapp so that you can send each a request for their loaded class information. You could then create a new webapp that combines the responses from the existing webapps for display.

If you don't need this information in some nice UI then the Sun JVM has number of -XX vm options that will show you what's going on with regards to class loading.

http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

I'm not that familiar with JRockit but I'd be surprised if it didn't have similar options.

Nick Holt
A: 

Ok I managed to get this working on Weblogic. Again my aim was to get java package names in all applications deployed on a given WebLogic server. Why? I had my reasons :)

First you have to get hold of the ear, war or jar file locations of all deployed apps. To do this we get the AppDeployment MBeans from WebLogic and iterate as shown below.

    Set<ObjectName> set = utils.getConfigMBeansByType("AppDeployment");
 for (ObjectName objectName : set) {
  String name = objectName.getKeyProperty("Name");   

  if (!appCache.contains(name)) {
   //System.out.println("Config bean: " + objectName);
   Object path = utils.getPropertyValue(objectName,
     "AbsoluteSourcePath");
   //System.out.println("Path: " + path);
   if(path != null){
    PackageFinder finder = new PackageFinder();
    packages.addAll(finder.findPackages(path.toString()));
   }
   appCache.add(name);
  }
 }

In the above code we get the path to the war, ear, jar or the exploded folder and passes it to the PackageFinder class's findPakages method which does all the work.

 public Set<String> findPackages(String path){
 File file = new File(path);
 if(file.exists() && file.isFile()){
  InputStream in = null;
  try {
   in = new BufferedInputStream(new FileInputStream(file));
   if(path.toLowerCase().endsWith(".war")){
    processWar(in);
   }else if(path.toLowerCase().endsWith(".ear")){
    processEar(in);
   }/*
    Rest of the method body removed, I guess you get the idea 
    */              
 return packageNames;

}


public void processJar(InputStream in){
 ZipInputStream zin = null;
 try {
  zin = new ZipInputStream(in);
  ZipEntry entry;
  while((entry = zin.getNextEntry()) != null){
   if(entry.getName().endsWith(".class")){
    addPackage(entry.getName());
   }
  }
 } catch (Exception e) {
 }
}
maneesh
Note: This does not return package names of all loaded classes in the JVM. It just returns all package names of classes bundled with all deployed apps in a WebLogic server, which serves my purpose.
maneesh