1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.geronimo.console.classloaderview; 18 19 import java.util.ArrayList; 20 import java.util.Collections; 21 import java.util.Comparator; 22 import java.util.HashMap; 23 import java.util.Iterator; 24 import java.util.List; 25 import java.util.Map; 26 27 import org.apache.geronimo.console.util.TreeEntry; 28 import org.apache.geronimo.kernel.config.MultiParentClassLoader; 29 import org.apache.geronimo.kernel.util.ClassLoaderRegistry; 30 import org.directwebremoting.annotations.RemoteMethod; 31 import org.directwebremoting.annotations.RemoteProxy; 32 33 @RemoteProxy 34 public class ClassLoaderViewHelper { 35 Map<String, TreeEntry> nodeHash; 36 private static final String NO_CHILD = "none"; 37 38 private static final String NORMAL_TYPE = "normal"; 39 40 private static final CmpTreeEntry cmp = new CmpTreeEntry(); 41 42 @RemoteMethod 43 public String getTrees(boolean inverse) { 44 nodeHash = new HashMap<String, TreeEntry>(); 45 46 List list = ClassLoaderRegistry.getList(); 47 Iterator iter = list.iterator(); 48 while (iter.hasNext()) { 49 if (!inverse) 50 updateTree((ClassLoader) iter.next()); 51 else 52 inverseTree((ClassLoader) iter.next()); 53 } 54 55 return this.printClassLoaders(); 56 } 57 58 public TreeEntry inverseTree(ClassLoader classloader) { 59 TreeEntry node = nodeHash.get(classloader.toString()); 60 if (null != node) 61 return node; 62 node = new TreeEntry(classloader.toString(), "root"); 63 node = addClasses(node, classloader); 64 nodeHash.put(node.getName(), node); 65 66 if (classloader instanceof MultiParentClassLoader) { 67 MultiParentClassLoader mpclassloader = (MultiParentClassLoader) classloader; 68 ClassLoader[] parents = mpclassloader.getParents(); 69 if (null != parents && 0 < parents.length) { 70 for (int i = 0; i < parents.length; i++) { 71 TreeEntry parentNode = inverseTree(parents[i]); 72 node.addChild(parentNode); 73 } 74 } 75 } else if (classloader.getParent() != null) { 76 TreeEntry parentNode = inverseTree(classloader.getParent()); 77 node.addChild(parentNode); 78 } 79 80 return node; 81 } 82 83 public TreeEntry updateTree(ClassLoader classloader) { 84 85 TreeEntry node = nodeHash.get(classloader.toString()); 86 if (null != node) 87 return node; 88 89 node = new TreeEntry(classloader.toString(), NORMAL_TYPE); 90 node = addClasses(node, classloader); 91 nodeHash.put(node.getName(), node); 92 93 if (classloader instanceof MultiParentClassLoader) { 94 MultiParentClassLoader mpclassloader = (MultiParentClassLoader) classloader; 95 ClassLoader[] parents = mpclassloader.getParents(); 96 if (null != parents && 0 < parents.length) { 97 for (int i = 0; i < parents.length; i++) { 98 TreeEntry parentNode = updateTree(parents[i]); 99 parentNode.addChild(node); 100 } 101 } 102 } else if (classloader.getParent() != null) { 103 TreeEntry parentNode = updateTree(classloader.getParent()); 104 parentNode.addChild(node); 105 } else { 106 node.setType("root"); 107 } 108 109 return node; 110 } 111 112 private TreeEntry addClasses(TreeEntry node, ClassLoader loader) { 113 try { 114 java.lang.reflect.Field CLASSES_VECTOR_FIELD = ClassLoader.class.getDeclaredField("classes"); 115 116 if (CLASSES_VECTOR_FIELD.getType() != java.util.Vector.class) { 117 return node; 118 } 119 CLASSES_VECTOR_FIELD.setAccessible(true); 120 121 final java.util.Vector classes = (java.util.Vector) CLASSES_VECTOR_FIELD.get(loader); 122 if (classes == null) 123 return node; 124 125 final Class[] result; 126 127 synchronized (classes) { 128 result = new Class[classes.size()]; 129 classes.toArray(result); 130 } 131 132 CLASSES_VECTOR_FIELD.setAccessible(false); 133 134 TreeEntry classNames = new TreeEntry("Classes", NORMAL_TYPE); 135 TreeEntry interfaceNames = new TreeEntry("Interfaces", NORMAL_TYPE); 136 node.addChild(classNames); 137 node.addChild(interfaceNames); 138 139 for (int i = 0; i < result.length; i++) { 140 if (result[i].isInterface()) 141 interfaceNames.addChild(new TreeEntry(result[i].toString(), NORMAL_TYPE)); 142 else 143 classNames.addChild(new TreeEntry(result[i].toString(), NORMAL_TYPE)); 144 } 145 if (classNames.getChildren().size() < 1) 146 classNames.addChild(new TreeEntry(NO_CHILD, NORMAL_TYPE)); 147 if (interfaceNames.getChildren().size() < 1) 148 interfaceNames.addChild(new TreeEntry(NO_CHILD, NORMAL_TYPE)); 149 return node; 150 } catch (Exception e) { 151 return node; 152 } 153 } 154 155 /* 156 * Usually we can directly give the java objects to clients via dwr, but we found that it is too slow when the 157 * objects become larger and more complex because of the bad efficiency of dwr. So we need to translate the java 158 * object to json text by hand in such cases. But it is more specific to the implementation of dojo, so it is not 159 * very recommended. 160 */ 161 String printClassLoaders() { 162 // generate an ordered id 163 List<TreeEntry> rootNodes = new ArrayList<TreeEntry>(); 164 for (TreeEntry entry : nodeHash.values()) { 165 if (entry.getType().equals("root")) 166 rootNodes.add(entry); 167 } 168 markupId(-1, rootNodes); // here root nodes have already been sorted 169 170 List<TreeEntry> allNodes = rootNodes; 171 for (TreeEntry entry : nodeHash.values()) { 172 if (!entry.getType().equals("root")) 173 allNodes.add(entry); 174 } 175 StringBuilder sb = new StringBuilder(512); 176 177 sb.append("{label:\"name\",identifier:\"id\",items:["); 178 Iterator<TreeEntry> list = allNodes.iterator(); 179 while (list.hasNext()) { 180 TreeEntry curr = list.next(); 181 sb.append("{name:\"").append(curr.getName()) 182 .append("\",id:\"").append(curr.getId()) 183 .append("\",type:\"").append(curr.getType()) 184 .append("\",children:["); 185 186 Iterator<TreeEntry> children = curr.getChildren().iterator(); 187 // the first child is Classes and the second one is Interfaces 188 printClasses(sb, children.next()); 189 sb.append(","); 190 printClasses(sb, children.next()); 191 while (children.hasNext()) { 192 TreeEntry child = children.next(); 193 sb.append(",{_reference:\"").append(child.getId()).append("\"}"); 194 } 195 if (list.hasNext()) 196 sb.append("]},"); 197 else 198 sb.append("]}"); 199 } 200 sb.append("]}"); 201 return sb.toString(); 202 } 203 204 private void printClasses(StringBuilder sb, TreeEntry classes) { 205 sb.append("{name:\"").append(classes.getName()) 206 .append("\",id:\"").append(classes.getId()) 207 .append("\",children:["); 208 Iterator<TreeEntry> children = classes.getChildren().iterator(); 209 while (children.hasNext()) { 210 TreeEntry child = children.next(); 211 sb.append("{name:\"").append(child.getName()) 212 .append("\",id:\"").append(child.getId()) 213 .append("\"}"); 214 if (children.hasNext()) 215 sb.append(","); 216 } 217 sb.append("]}"); 218 } 219 220 private int markupId(int pre, List<TreeEntry> list) { 221 Collections.sort(list, cmp); 222 for (TreeEntry child : list) { 223 if (null == child.getId()) 224 child.setId(String.valueOf(++pre)); 225 pre = markupId(pre, child.getChildren()); 226 } 227 return pre; 228 } 229 230 static class CmpTreeEntry implements Comparator<TreeEntry> { 231 public int compare(TreeEntry x, TreeEntry y) { 232 if (x.getName() == null) 233 return -1; 234 return x.getName().compareTo(y.getName()); 235 } 236 } 237 }