Coverage Report - sk.baka.webvm.Classloaders
 
Classes in this File Line Coverage Branch Coverage Complexity
Classloaders
0%
0/75
0%
0/24
2.429
Classloaders$1
N/A
N/A
2.429
Classloaders$ClassloaderClashes
0%
0/8
N/A
2.429
Classloaders$ClassloaderHierarchyTree
0%
0/15
0%
0/6
2.429
Classloaders$DirAndNameComparator
0%
0/7
0%
0/6
2.429
Classloaders$Listener
0%
0/14
0%
0/2
2.429
Classloaders$TreeNode
0%
0/3
0%
0/2
2.429
 
 1  
 /**
 2  
  * Copyright 2009 Martin Vysny.
 3  
  *
 4  
  * This file is part of WebVM.
 5  
  *
 6  
  * WebVM is free software: you can redistribute it and/or modify
 7  
  * it under the terms of the GNU General Public License as published by
 8  
  * the Free Software Foundation, either version 3 of the License, or
 9  
  * (at your option) any later version.
 10  
  *
 11  
  * WebVM is distributed in the hope that it will be useful,
 12  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  
  * GNU General Public License for more details.
 15  
  *
 16  
  * You should have received a copy of the GNU General Public License
 17  
  * along with WebVM.  If not, see <http://www.gnu.org/licenses/>.
 18  
  */
 19  
 package sk.baka.webvm;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.Serializable;
 23  
 import java.net.URI;
 24  
 import java.net.URISyntaxException;
 25  
 import java.net.URL;
 26  
 import java.net.URLClassLoader;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Collections;
 29  
 import java.util.Comparator;
 30  
 import java.util.HashMap;
 31  
 import java.util.List;
 32  
 import java.util.Map;
 33  
 import java.util.logging.Level;
 34  
 import java.util.logging.Logger;
 35  
 import javax.swing.tree.DefaultMutableTreeNode;
 36  
 import javax.swing.tree.DefaultTreeModel;
 37  
 import javax.swing.tree.MutableTreeNode;
 38  
 import javax.swing.tree.TreeModel;
 39  
 import org.apache.wicket.ajax.AjaxRequestTarget;
 40  
 import org.apache.wicket.markup.html.basic.Label;
 41  
 import org.apache.wicket.markup.html.list.ListItem;
 42  
 import org.apache.wicket.markup.html.list.ListView;
 43  
 import org.apache.wicket.markup.html.tree.BaseTree;
 44  
 import org.apache.wicket.markup.html.tree.BaseTree.LinkType;
 45  
 import org.apache.wicket.markup.html.tree.ITreeStateListener;
 46  
 import org.apache.wicket.markup.html.tree.LabelTree;
 47  
 import org.apache.wicket.markup.html.tree.LinkTree;
 48  
 import sk.baka.tools.UrlUtils;
 49  
 import sk.baka.webvm.analyzer.classloader.CLEnum;
 50  
 import sk.baka.webvm.analyzer.classloader.ClassLoaderUtils;
 51  
 import sk.baka.webvm.analyzer.classloader.ResourceLink;
 52  
 import sk.baka.webvm.wicket.WicketUtils;
 53  
 
 54  
 /**
 55  
  * Performs the class loader analysis.
 56  
  * @author Martin Vysny
 57  
  */
 58  0
 public final class Classloaders extends WebVMPage {
 59  
 
 60  
     private static final long serialVersionUID = 1L;
 61  
 
 62  
     /**
 63  
      * Constructor.
 64  
      */
 65  0
     public Classloaders() {
 66  0
         tree = newClassloaderHierarchy();
 67  0
         analyzeClashes(border);
 68  0
         border.add(tree);
 69  0
     }
 70  
     private LabelTree tree;
 71  
 
 72  
     private void analyzeClashes(AppBorder border) {
 73  0
         final Map<URI, List<Integer>> clashes = getClashes();
 74  0
         final List<URI> uris = new ArrayList<URI>(clashes.keySet());
 75  0
         border.add(new ClassloaderClashes("clRow", uris, clashes));
 76  0
     }
 77  
 
 78  
     private Map<URI, List<Integer>> getClashes() {
 79  0
         final List<ClassLoader> cls = ClassLoaderUtils.getClassLoaderChain(Thread.currentThread().getContextClassLoader());
 80  
         final Map<URI, List<ClassLoader>> clashes;
 81  
         try {
 82  0
             clashes = ClassLoaderUtils.getClassLoaderURIs(Thread.currentThread().getContextClassLoader());
 83  0
         } catch (URISyntaxException ex) {
 84  0
             throw new RuntimeException(ex);
 85  0
         }
 86  0
         ClassLoaderUtils.filterClashes(clashes);
 87  0
         final Map<URI, List<Integer>> result = new HashMap<URI, List<Integer>>();
 88  0
         for (final Map.Entry<URI, List<ClassLoader>> e : clashes.entrySet()) {
 89  0
             final List<Integer> clNumbers = new ArrayList<Integer>();
 90  0
             for (final ClassLoader cl : e.getValue()) {
 91  0
                 clNumbers.add(cls.indexOf(cl) + 1);
 92  
             }
 93  0
             result.put(e.getKey(), clNumbers);
 94  0
         }
 95  0
         return result;
 96  
     }
 97  
 
 98  
     private LabelTree newClassloaderHierarchy() {
 99  0
         final MutableTreeNode root = new DefaultMutableTreeNode("root");
 100  0
         analyzeClassloader(root, Thread.currentThread().getContextClassLoader());
 101  0
         final TreeModel model = new DefaultTreeModel(root);
 102  0
         final LinkTree result = new ClassloaderHierarchyTree("classloaderHierarchy", model);
 103  0
         result.setRootLess(true);
 104  
         // resource download does not work with AJAX links
 105  
         // @TODO fix this - provide regular links for downloads, ajax links for anything else
 106  0
         result.setLinkType(LinkType.REGULAR);
 107  0
         result.invalidateAll();
 108  0
         result.getTreeState().addTreeStateListener(new Listener());
 109  0
         return result;
 110  
     }
 111  
 
 112  
     private static void expandNode(final Object node, final DefaultTreeModel tree) {
 113  0
         final DefaultMutableTreeNode n = (DefaultMutableTreeNode) node;
 114  0
         if (!(n.getUserObject() instanceof ResourceLink)) {
 115  0
             return;
 116  
         }
 117  0
         final ResourceLink parent = (ResourceLink) n.getUserObject();
 118  0
         if (!parent.isPackage()) {
 119  0
             return;
 120  
         }
 121  0
         n.removeAllChildren();
 122  
         try {
 123  0
             final List<ResourceLink> children = parent.listAndGroup();
 124  0
             Collections.sort(children, new DirAndNameComparator());
 125  0
             for (final ResourceLink link : children) {
 126  0
                 n.add(new TreeNode(link));
 127  
             }
 128  0
         } catch (Exception ex) {
 129  0
             LOG.log(Level.SEVERE, "Error while retrieving resources", ex);
 130  0
             n.add(new DefaultMutableTreeNode("Error while retrieving resources: " + ex.toString()));
 131  0
         }
 132  0
         tree.nodeStructureChanged(n);
 133  0
     }
 134  
 
 135  
     private DefaultTreeModel getModel() {
 136  0
         return (DefaultTreeModel) tree.getModelObject();
 137  
     }
 138  
 
 139  0
     private class Listener implements ITreeStateListener, Serializable {
 140  
 
 141  
         private static final long serialVersionUID = 1L;
 142  
 
 143  
         public void allNodesCollapsed() {
 144  
             // do nothing
 145  0
         }
 146  
 
 147  
         public void allNodesExpanded() {
 148  
             // do nothing
 149  0
         }
 150  
 
 151  
         public void nodeCollapsed(Object node) {
 152  0
             final DefaultMutableTreeNode n = (DefaultMutableTreeNode) node;
 153  0
             if (!(n.getUserObject() instanceof ResourceLink)) {
 154  0
                 return;
 155  
             }
 156  
             // cleanup children
 157  0
             n.removeAllChildren();
 158  0
             final DefaultTreeModel model = getModel();
 159  0
             model.nodeStructureChanged(n);
 160  0
         }
 161  
 
 162  
         public void nodeExpanded(Object node) {
 163  0
             expandNode(node, getModel());
 164  0
         }
 165  
 
 166  
         public void nodeSelected(Object node) {
 167  
             // do nothing
 168  0
         }
 169  
 
 170  
         public void nodeUnselected(Object node) {
 171  
             // do nothing
 172  0
         }
 173  
     }
 174  0
     private static final Logger LOG = Logger.getLogger(Classloaders.class.getName());
 175  
     private static final int MAX_CLASSLOADER_NAME_LENGTH = 60;
 176  
 
 177  
     private static class TreeNode extends DefaultMutableTreeNode {
 178  
 
 179  
         private static final long serialVersionUID = 1L;
 180  
 
 181  
         public TreeNode(final ResourceLink link) {
 182  0
             super(link);
 183  0
         }
 184  
 
 185  
         @Override
 186  
         public boolean isLeaf() {
 187  0
             return !((ResourceLink) getUserObject()).isPackage();
 188  
         }
 189  
     }
 190  
 
 191  
     private static void analyzeClassloader(final MutableTreeNode root, final ClassLoader cl) {
 192  0
         ClassLoader current = cl;
 193  0
         int clNumber = 1;
 194  0
         while (current != null) {
 195  0
             final DefaultMutableTreeNode result = new DefaultMutableTreeNode();
 196  0
             String name = current.toString();
 197  0
             if (name.length() > MAX_CLASSLOADER_NAME_LENGTH) {
 198  0
                 name = name.substring(0, MAX_CLASSLOADER_NAME_LENGTH) + "...";
 199  
             }
 200  0
             result.setUserObject("[" + (clNumber++) + "] " + CLEnum.getTypes(current) + " " + current.getClass().getName() + ": " + name);
 201  0
             if (current instanceof URLClassLoader) {
 202  0
                 addClassLoaderURLs(result, (URLClassLoader) current);
 203  
             }
 204  0
             root.insert(result, root.getChildCount());
 205  0
             current = current.getParent();
 206  0
         }
 207  0
     }
 208  
 
 209  
     private static void addClassLoaderURLs(final DefaultMutableTreeNode result, final URLClassLoader cl) {
 210  0
         final URL[] clUrls = cl.getURLs();
 211  0
         if (clUrls == null || clUrls.length == 0) {
 212  0
             result.add(new DefaultMutableTreeNode("URLClassLoader.getURLs() is empty"));
 213  0
             return;
 214  
         }
 215  0
         for (final URL url : clUrls) {
 216  0
             final File file = toFile(url);
 217  0
             final DefaultMutableTreeNode node = (file == null) ? new DefaultMutableTreeNode(url) : new TreeNode(ResourceLink.newFor(file));
 218  0
             result.add(node);
 219  
         }
 220  0
     }
 221  
 
 222  
     /**
 223  
      * Shows class/resource clashes between different classloaders.
 224  
      */
 225  
     private static class ClassloaderClashes extends ListView<URI> {
 226  
 
 227  
         private static final long serialVersionUID = 1L;
 228  
         private final Map<URI, List<Integer>> clashes;
 229  
 
 230  
         public ClassloaderClashes(String id, List<? extends URI> list, Map<URI, List<Integer>> clashes) {
 231  0
             super(id, list);
 232  0
             this.clashes = clashes;
 233  0
         }
 234  
 
 235  
         @Override
 236  
         protected void populateItem(final ListItem<URI> item) {
 237  0
             final URI uri = item.getModelObject();
 238  0
             item.add(new Label("clURI", uri.toString()));
 239  0
             final List<Integer> clNumbers = clashes.get(uri);
 240  0
             item.add(new Label("clClassLoader", clNumbers.toString()));
 241  0
         }
 242  
     }
 243  
 
 244  
     /**
 245  
      * A tree component showing the classloader hierarchy.
 246  
      */
 247  
     private static class ClassloaderHierarchyTree extends LinkTree {
 248  
 
 249  
         private static final long serialVersionUID = 1L;
 250  
 
 251  
         public ClassloaderHierarchyTree(String id, TreeModel model) {
 252  0
             super(id, model);
 253  0
         }
 254  
 
 255  
         @Override
 256  
         protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target) {
 257  0
             final DefaultMutableTreeNode n = (DefaultMutableTreeNode) node;
 258  0
             if (!(n.getUserObject() instanceof ResourceLink)) {
 259  0
                 tree.getTreeState().expandNode(node);
 260  0
                 return;
 261  
             }
 262  0
             final ResourceLink parent = (ResourceLink) n.getUserObject();
 263  0
             final boolean redirected = WicketUtils.redirectTo(parent);
 264  0
             if (redirected) {
 265  0
                 return;
 266  
             }
 267  0
             if (parent.isPackage()) {
 268  0
                 expandNode(node, (DefaultTreeModel) getModelObject());
 269  0
                 tree.getTreeState().expandNode(node);
 270  0
                 return;
 271  
             }
 272  0
         }
 273  
     }
 274  
 
 275  
     /**
 276  
      * Sorts ResourceLinks, packages first, then by a name.
 277  
      */
 278  0
     private static class DirAndNameComparator implements Comparator<ResourceLink> {
 279  
 
 280  
         public int compare(ResourceLink o1, ResourceLink o2) {
 281  0
             if (o1.isPackage()) {
 282  0
                 if (!o2.isPackage()) {
 283  0
                     return -1;
 284  
                 }
 285  
             } else {
 286  0
                 if (o2.isPackage()) {
 287  0
                     return 1;
 288  
                 }
 289  
             }
 290  0
             return o1.getName().compareToIgnoreCase(o2.getName());
 291  
         }
 292  
     }
 293  
 }