Coverage Report - sk.baka.webvm.Jndi
 
Classes in this File Line Coverage Branch Coverage Complexity
Jndi
0%
0/59
0%
0/10
2.857
Jndi$1
0%
0/7
0%
0/2
2.857
Jndi$2
0%
0/2
N/A
2.857
Jndi$JndiTreeNode
0%
0/57
0%
0/22
2.857
 
 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.lang.reflect.Proxy;
 22  
 import java.util.Collections;
 23  
 import java.util.Comparator;
 24  
 import java.util.List;
 25  
 import java.util.logging.Level;
 26  
 import java.util.logging.Logger;
 27  
 import javax.naming.Context;
 28  
 import javax.naming.InitialContext;
 29  
 import javax.naming.LinkRef;
 30  
 import javax.naming.NameClassPair;
 31  
 import javax.naming.NamingEnumeration;
 32  
 import javax.naming.NamingException;
 33  
 import javax.swing.tree.DefaultMutableTreeNode;
 34  
 import javax.swing.tree.DefaultTreeModel;
 35  
 import javax.swing.tree.MutableTreeNode;
 36  
 import javax.swing.tree.TreeModel;
 37  
 import javax.swing.tree.TreeNode;
 38  
 import org.apache.wicket.markup.html.tree.LabelTree;
 39  
 import org.apache.wicket.model.LoadableDetachableModel;
 40  
 import sk.baka.tools.javaee.JeeServer;
 41  
 
 42  
 /**
 43  
  * Shows the JNDI tree.
 44  
  * @author Martin Vysny
 45  
  */
 46  0
 public final class Jndi extends WebVMPage {
 47  
 
 48  
     private static final long serialVersionUID = 1L;
 49  
 
 50  
     /**
 51  
      * Constructor.
 52  
      */
 53  0
     public Jndi() {
 54  0
         border.add(newJndiTree("jndiTree", null));
 55  0
         border.add(newJndiTree("jndiJavaTree", "java:comp"));
 56  0
     }
 57  
 
 58  
     private static LabelTree newJndiTree(final String treeId, final String context) {
 59  0
         final LabelTree tree = new LabelTree(treeId, new LoadableDetachableModel<TreeModel>() {
 60  
 
 61  
             private static final long serialVersionUID = 1L;
 62  
 
 63  
             @Override
 64  
             protected TreeModel load() {
 65  
                 try {
 66  0
                     Context c = new InitialContext();
 67  0
                     if (context != null) {
 68  0
                         c = (Context) c.lookup(context);
 69  
                     }
 70  0
                     return listJNDI(c);
 71  0
                 } catch (Exception ex) {
 72  0
                     return toModel(ex);
 73  
                 }
 74  
             }
 75  
         });
 76  0
         tree.getTreeState().expandAll();
 77  0
         tree.setRootLess(true);
 78  0
         return tree;
 79  
     }
 80  
 
 81  
     private static TreeModel toModel(Exception ex) {
 82  0
         final TreeNode root = new DefaultMutableTreeNode("Error listing JNDI tree: " + ex.toString());
 83  0
         return new DefaultTreeModel(root);
 84  
     }
 85  
 
 86  
     /**
 87  
      * Lists the JNDI context and returns it as a tree.
 88  
      * @param c the context to list
 89  
      * @return JNDI pretty-printed JNDI tree
 90  
      */
 91  
     public static TreeModel listJNDI(final Context c) {
 92  0
         final MutableTreeNode root = new DefaultMutableTreeNode("root");
 93  0
         list(c, root, 0);
 94  0
         return new DefaultTreeModel(root);
 95  
     }
 96  
 
 97  
     /**
 98  
      * Prints the context recursively.
 99  
      *
 100  
      * @param ctx
 101  
      *            the context contents to print.
 102  
      * @param parent print children of this parent.
 103  
      * @param depth current depth.
 104  
      */
 105  
     private static void list(final Context ctx, final MutableTreeNode parent, final int depth) {
 106  
         try {
 107  0
             final NamingEnumeration<NameClassPair> ne = ctx.list("");
 108  
             try {
 109  
                 // sort the JNDI listing first
 110  0
                 final List<NameClassPair> children = Collections.list(ne);
 111  0
                 Collections.sort(children, new Comparator<NameClassPair>() {
 112  
 
 113  
                     public int compare(NameClassPair o1, NameClassPair o2) {
 114  0
                         return o1.getName().compareToIgnoreCase(o2.getName());
 115  
                     }
 116  
                 });
 117  0
                 for (final NameClassPair pair : children) {
 118  
                     try {
 119  0
                         listUnprotected(ctx, parent, pair, depth);
 120  0
                     } catch (final Exception e) {
 121  0
                         LOG.log(Level.SEVERE, "JNDI examination error", e);
 122  0
                         final StringBuilder sb = new StringBuilder();
 123  0
                         sb.append(pair.getName());
 124  0
                         sb.append(": Failed to examine: ");
 125  0
                         sb.append(e.toString());
 126  0
                         addWarningNode(parent, sb.toString());
 127  0
                     }
 128  
                 }
 129  
             } finally {
 130  0
                 closeQuietly(ne);
 131  0
             }
 132  0
         } catch (Exception ex) {
 133  0
             LOG.log(Level.SEVERE, "JNDI examination error", ex);
 134  0
             String name = "[unknown]";
 135  
             try {
 136  0
                 name = ctx.getNameInNamespace();
 137  0
             } catch (Exception e) {
 138  
                 // do nothing
 139  0
             }
 140  0
             addWarningNode(parent, name + ": Failed to examine: " + ex.toString());
 141  0
         }
 142  0
     }
 143  
     /**
 144  
      * Enumerates the JNDI tree up to this depth.
 145  
      */
 146  
     private static final int MAX_DEPTH = 5;
 147  
 
 148  
     private static void listUnprotected(final Context ctx, final MutableTreeNode parent, final NameClassPair pair, final int depth)
 149  
             throws NamingException, ClassNotFoundException {
 150  0
         final JndiTreeNode node = new JndiTreeNode(ctx, pair);
 151  0
         if (node.isContext) {
 152  
             //sometimes there is an StackOverflow exception, we rather check it with primitive condition
 153  0
             if (depth < MAX_DEPTH) {
 154  0
                 final Object value = ctx.lookup(jndiFix(pair.getName()));
 155  0
                 final Context subctx = (Context) value;
 156  0
                 list(subctx, node, depth + 1);
 157  0
             } else {
 158  0
                 addWarningNode(node, "Maximum depth of " + MAX_DEPTH + " reached");
 159  
             }
 160  
         }
 161  0
         parent.insert(node, parent.getChildCount());
 162  0
     }
 163  
 
 164  
     private static void addWarningNode(final MutableTreeNode node,
 165  
             final String text) {
 166  0
         node.insert(new DefaultMutableTreeNode(text), node.getChildCount());
 167  0
     }
 168  
 
 169  
     /**
 170  
      * Closes given object quietly. Any exceptions are logged using debug level.
 171  
      *
 172  
      * @param c
 173  
      *            the object to close.
 174  
      */
 175  
     public static void closeQuietly(final NamingEnumeration<?> c) {
 176  
         try {
 177  0
             c.close();
 178  0
         } catch (final Exception ex) {
 179  0
             LOG.log(Level.FINE, "Failed to close an object", ex);
 180  0
         }
 181  0
     }
 182  0
     private static final Logger LOG = Logger.getLogger(Jndi.class.getName());
 183  
 
 184  
     /**
 185  
      * A model object which constructs itself from given JNDI information.
 186  
      */
 187  0
     private static class JndiTreeNode extends DefaultMutableTreeNode {
 188  
 
 189  
         private static final long serialVersionUID = 1L;
 190  
         /**
 191  
          * true if given nameclass pair is a context which may contain other pairs.
 192  
          */
 193  0
         private boolean isContext = false;
 194  
         /**
 195  
          * true if given nameclass pair is a reference object.
 196  
          */
 197  0
         private boolean isLinkRef = false;
 198  
         /**
 199  
          * true if given nameclass pair is a proxy object.
 200  
          */
 201  0
         private boolean isProxy = false;
 202  
         /**
 203  
          * true if given nameclass pair class was not found.
 204  
          */
 205  0
         private Throwable classLoadFailure = null;
 206  
 
 207  
         /**
 208  
          * Creates new node model object from given JNDI pair.
 209  
          * @param ctx the context instance
 210  
          * @param pair the name class pair
 211  
          * @throws javax.naming.NamingException
 212  
          */
 213  
         public JndiTreeNode(final Context ctx, final NameClassPair pair) throws NamingException {
 214  0
             super();
 215  0
             final StringBuilder sb = new StringBuilder();
 216  0
             final String name = jndiFix(pair.getName());
 217  0
             sb.append(name);
 218  0
             Class<?> clazz = null;
 219  
             try {
 220  0
                 clazz = loadClass(pair, ctx);
 221  0
                 isContext = Context.class.isAssignableFrom(clazz);
 222  0
                 isLinkRef = LinkRef.class.isAssignableFrom(clazz);
 223  0
                 isProxy = Proxy.isProxyClass(clazz) || pair.getClassName().startsWith("$Proxy");
 224  0
             } catch (ClassNotFoundException ex) {
 225  0
                 classLoadFailure = ex;
 226  0
             }
 227  
             // Display reference targets
 228  0
             if (isLinkRef) {
 229  0
                 final Object obj = ctx.lookupLink(name);
 230  0
                 final LinkRef link = (LinkRef) obj;
 231  0
                 sb.append("[link -> ");
 232  0
                 sb.append(link.getLinkName());
 233  0
                 sb.append(']');
 234  
             }
 235  
             // Display proxy interfaces
 236  0
             if (isProxy) {
 237  0
                 sb.append(" (proxy: " + pair.getClassName());
 238  0
                 final Class<?>[] ifaces = clazz.getInterfaces();
 239  0
                 sb.append(" implements ");
 240  0
                 for (int i = 0; i < ifaces.length; i++) {
 241  0
                     sb.append(ifaces[i]);
 242  0
                     sb.append(',');
 243  
                 }
 244  0
                 sb.setCharAt(sb.length() - 1, ')');
 245  0
             } else if (isContext) {
 246  0
                 sb.append(" (Context)");
 247  
             } else {
 248  0
                 sb.append(" (class: ");
 249  0
                 sb.append(pair.getClassName());
 250  0
                 sb.append(")");
 251  
             }
 252  0
             if (classLoadFailure != null) {
 253  0
                 sb.append(" - failed to load class: ");
 254  0
                 sb.append(classLoadFailure.toString());
 255  
             } else {
 256  0
                 if (String.class.isAssignableFrom(clazz)) {
 257  0
                     sb.append(": ");
 258  0
                     final String str = (String) ctx.lookup(name);
 259  0
                     if (str == null) {
 260  0
                         sb.append("null");
 261  
                     } else {
 262  0
                         sb.append("\"");
 263  0
                         sb.append(str);
 264  0
                         sb.append("\"");
 265  
                     }
 266  
                 }
 267  
             }
 268  0
             setUserObject(sb.toString());
 269  0
         }
 270  
 
 271  
         /**
 272  
          * Try to get the class from given JNDI pair.
 273  
          *
 274  
          * @param pair
 275  
          *            the pair
 276  
          * @param ctx
 277  
          *            the context
 278  
          * @return non-<code>null</code> class instance.
 279  
          * @throws ClassNotFoundException
 280  
          * @throws NamingException
 281  
          */
 282  
         private static Class<?> loadClass(final NameClassPair pair,
 283  
                 final Context ctx) throws ClassNotFoundException, NamingException {
 284  0
             final ClassLoader loader = Thread.currentThread().getContextClassLoader();
 285  
             try {
 286  0
                 return loader.loadClass(pair.getClassName());
 287  0
             } catch (ClassNotFoundException e) {
 288  0
                 if (!pair.getClassName().startsWith("$Proxy")) {
 289  0
                     throw e;
 290  
                 }
 291  
             // try some other methods of obtaining the class.
 292  
             }
 293  
             // We have to get the class from the binding
 294  0
             final Object p = ctx.lookup(pair.getName());
 295  0
             return p.getClass();
 296  
         }
 297  
 
 298  
         @Override
 299  
         public boolean isLeaf() {
 300  0
             return !isContext;
 301  
         }
 302  
     }
 303  
     static String jndiFix(String name) {
 304  
         // workaround: Glassfish returns java:comp/env as a single child of java:comp. It should return just env instead. Fix that.
 305  0
         if (JeeServer.getRuntimeNull() == JeeServer.Glassfish && name.equals("java:comp/env")) {
 306  0
             return "env";
 307  
         }
 308  0
         return name;
 309  
     }
 310  
 }