1 /*
   2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.File;
  25 import java.lang.ref.WeakReference;
  26 import java.net.URL;
  27 import java.net.URLClassLoader;
  28 import javax.naming.Context;
  29 import javax.naming.InitialContext;
  30 import javax.naming.NamingException;
  31 import javax.naming.NoInitialContextException;
  32 import javax.naming.spi.InitialContextFactory;
  33 import javax.naming.spi.NamingManager;
  34 import java.util.Hashtable;
  35 
  36 public class DummyContextFactory implements InitialContextFactory {
  37     static final String DUMMY_FACTORY = "DummyContextFactory";
  38     static final String DUMMY_FACTORY2 = "DummyContextFactory2";
  39     static final String MISSING_FACTORY = "NonExistant";
  40     static int counter = 0;
  41     ClassLoader origContextLoader = Thread.currentThread().getContextClassLoader();
  42 
  43     public static void main(String[] s) throws Exception {
  44         DummyContextFactory dcf = new DummyContextFactory();
  45         dcf.runTest();
  46     }
  47 
  48     private void runTest() throws Exception {
  49         final String classes = System.getProperty("url.dir", ".");
  50         final URL curl = new File(classes).toURI().toURL();
  51         URLClassLoader testLoader = new URLClassLoader(new URL[] {curl}, null);
  52         WeakReference<URLClassLoader> weakRef = new WeakReference<>(testLoader);
  53         Thread.currentThread().setContextClassLoader(testLoader);
  54         Hashtable<String, String> env = new Hashtable<>();
  55         env.put(Context.INITIAL_CONTEXT_FACTORY, DUMMY_FACTORY);
  56         testContextCalls(env);
  57 
  58         // now test with another factory
  59         Thread.currentThread().setContextClassLoader(testLoader);
  60         env.put(Context.INITIAL_CONTEXT_FACTORY, DUMMY_FACTORY2);
  61         testContextCalls(env);
  62 
  63         // one count is derived from a default constructor call (ignored for test)
  64         // class associated with this ClassLoader should have 2 counts
  65         if (counter != 2) {
  66             throw new RuntimeException("wrong count: " + counter);
  67         }
  68 
  69         // a test for handling non-existent classes
  70         env.put(Context.INITIAL_CONTEXT_FACTORY, MISSING_FACTORY);
  71         testBadContextCall(env);
  72 
  73         // test that loader gets GC'ed
  74         testLoader = null;
  75         System.gc();
  76         while (weakRef.get() != null) {
  77             Thread.sleep(100);
  78             System.gc();
  79         }
  80     }
  81 
  82     private void testContextCalls(Hashtable<String, String> env) throws Exception {
  83         // the context is returned here but it's the ContextFactory that
  84         // we're mainly interested in. Hence the counter test.
  85 
  86         // 1st call populates the WeakHashMap
  87         // Uses URLClassLoader
  88         Context cxt = NamingManager.getInitialContext(env);
  89 
  90         // 2nd call uses cached factory
  91         cxt = NamingManager.getInitialContext(env);
  92 
  93         Thread.currentThread().setContextClassLoader(origContextLoader);
  94 
  95         // 3rd call uses new factory
  96         // AppClassLoader
  97         cxt = NamingManager.getInitialContext(env);
  98 
  99         // test with null TCCL
 100         // this shouldn't increase the count since a null TCCL
 101         // means we default to System ClassLoader in this case (AppClassLoader)
 102         Thread.currentThread().setContextClassLoader(null);
 103         cxt = NamingManager.getInitialContext(env);
 104     }
 105 
 106     private void testBadContextCall(Hashtable<String, String> env) throws Exception {
 107         try {
 108             Context cxt = NamingManager.getInitialContext(env);
 109             throw new RuntimeException("Expected NoInitialContextException");
 110         } catch (NoInitialContextException e) {
 111             if (!(e.getCause() instanceof ClassNotFoundException)) {
 112                 throw new RuntimeException("unexpected cause", e.getCause());
 113             }
 114         }
 115     }
 116 
 117     public DummyContextFactory() {
 118         System.out.println("New DummyContextFactory " + (++counter));
 119         //new Throwable().printStackTrace(System.out);
 120     }
 121 
 122     @Override
 123     public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
 124         return new DummyContext(environment);
 125     }
 126 
 127     public class DummyContext extends InitialContext {
 128 
 129         private Hashtable<?, ?> env;
 130 
 131         DummyContext(Hashtable<?, ?> env) throws NamingException {
 132             this.env = env;
 133         }
 134 
 135         public Hashtable<?, ?> getEnvironment() {
 136             return env;
 137         }
 138     }
 139 }