1 /*
   2  * Copyright (c) 1999, 2014, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.naming.internal;
  27 
  28 import javax.naming.NamingEnumeration;
  29 import java.io.File;
  30 import java.io.FileInputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.net.MalformedURLException;
  34 import java.net.URL;
  35 import java.net.URLClassLoader;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.security.PrivilegedActionException;
  39 import java.security.PrivilegedExceptionAction;
  40 import java.util.*;
  41 
  42 /**
  43  * VersionHelper was used by JNDI to accommodate differences between
  44  * JDK 1.1.x and the Java 2 platform. As this is no longer necessary
  45  * since JNDI's inclusion in the platform, this class currently
  46  * serves as a set of utilities for performing system-level things,
  47  * such as class-loading and reading system properties.
  48  *
  49  * @author Rosanna Lee
  50  * @author Scott Seligman
  51  */
  52 
  53 public final class VersionHelper {
  54     private static final VersionHelper helper = new VersionHelper();
  55 
  56     final static String[] PROPS = new String[]{
  57         javax.naming.Context.INITIAL_CONTEXT_FACTORY,
  58         javax.naming.Context.OBJECT_FACTORIES,
  59         javax.naming.Context.URL_PKG_PREFIXES,
  60         javax.naming.Context.STATE_FACTORIES,
  61         javax.naming.Context.PROVIDER_URL,
  62         javax.naming.Context.DNS_URL,
  63         // The following shouldn't create a runtime dependence on ldap package.
  64         javax.naming.ldap.LdapContext.CONTROL_FACTORIES
  65     };
  66 
  67     public final static int INITIAL_CONTEXT_FACTORY = 0;
  68     public final static int OBJECT_FACTORIES = 1;
  69     public final static int URL_PKG_PREFIXES = 2;
  70     public final static int STATE_FACTORIES = 3;
  71     public final static int PROVIDER_URL = 4;
  72     public final static int DNS_URL = 5;
  73     public final static int CONTROL_FACTORIES = 6;
  74 
  75     private VersionHelper() {} // Disallow anyone from creating one of these.
  76 
  77     public static VersionHelper getVersionHelper() {
  78         return helper;
  79     }
  80 
  81     public Class<?> loadClass(String className) throws ClassNotFoundException {
  82         return loadClass(className, getContextClassLoader());
  83     }
  84 
  85     /**
  86      * @param className A non-null fully qualified class name.
  87      * @param codebase  A non-null, space-separated list of URL strings.
  88      */
  89     public Class<?> loadClass(String className, String codebase)
  90             throws ClassNotFoundException, MalformedURLException {
  91 
  92         ClassLoader parent = getContextClassLoader();
  93         ClassLoader cl =
  94                 URLClassLoader.newInstance(getUrlArray(codebase), parent);
  95 
  96         return loadClass(className, cl);
  97     }
  98 
  99     /**
 100      * Package private.
 101      * <p>
 102      * This internal method is used with Thread Context Class Loader (TCCL),
 103      * please don't expose this method as public.
 104      */
 105     Class<?> loadClass(String className, ClassLoader cl)
 106             throws ClassNotFoundException {
 107         Class<?> cls = Class.forName(className, true, cl);
 108         return cls;
 109     }
 110 
 111     /*
 112      * Returns a JNDI property from the system properties. Returns
 113      * null if the property is not set, or if there is no permission
 114      * to read it.
 115      */
 116     String getJndiProperty(int i) {
 117         PrivilegedAction<String> act = () -> {
 118             try {
 119                 return System.getProperty(PROPS[i]);
 120             } catch (SecurityException e) {
 121                 return null;
 122             }
 123         };
 124         return AccessController.doPrivileged(act);
 125     }
 126 
 127     /*
 128      * Reads each property in PROPS from the system properties, and
 129      * returns their values -- in order -- in an array.  For each
 130      * unset property, the corresponding array element is set to null.
 131      * Returns null if there is no permission to call System.getProperties().
 132      */
 133     String[] getJndiProperties() {
 134         PrivilegedAction<Properties> act = () -> {
 135             try {
 136                 return System.getProperties();
 137             } catch (SecurityException e) {
 138                 return null;
 139             }
 140         };
 141         Properties sysProps = AccessController.doPrivileged(act);
 142         if (sysProps == null) {
 143             return null;
 144         }
 145         String[] jProps = new String[PROPS.length];
 146         for (int i = 0; i < PROPS.length; i++) {
 147             jProps[i] = sysProps.getProperty(PROPS[i]);
 148         }
 149         return jProps;
 150     }
 151 
 152     /*
 153      * Returns the resource of a given name associated with a particular
 154      * class (never null), or null if none can be found.
 155      */
 156     InputStream getResourceAsStream(Class<?> c, String name) {
 157         PrivilegedAction<InputStream> act = () -> c.getResourceAsStream(name);
 158         return AccessController.doPrivileged(act);
 159     }
 160 
 161     /*
 162      * Returns an input stream for a file in <java.home>/lib,
 163      * or null if it cannot be located or opened.
 164      *
 165      * @param filename  The file name, sans directory.
 166      */
 167     InputStream getJavaHomeLibStream(String filename) {
 168         PrivilegedAction<InputStream> act = () -> {
 169             try {
 170                 String javahome = System.getProperty("java.home");
 171                 if (javahome == null) {
 172                     return null;
 173                 }
 174                 String pathname = javahome + File.separator +
 175                         "lib" + File.separator + filename;
 176                 return new FileInputStream(pathname);
 177             } catch (Exception e) {
 178                 return null;
 179             }
 180         };
 181         return AccessController.doPrivileged(act);
 182     }
 183 
 184     /*
 185      * Returns an enumeration (never null) of InputStreams of the
 186      * resources of a given name associated with a particular class
 187      * loader.  Null represents the bootstrap class loader in some
 188      * Java implementations.
 189      */
 190     NamingEnumeration<InputStream> getResources(ClassLoader cl,
 191                                                 String name) throws IOException {
 192         Enumeration<URL> urls;
 193         PrivilegedExceptionAction<Enumeration<URL>> act = () ->
 194                 (cl == null)
 195                         ? ClassLoader.getSystemResources(name)
 196                         : cl.getResources(name);
 197         try {
 198             urls = AccessController.doPrivileged(act);
 199         } catch (PrivilegedActionException e) {
 200             throw (IOException) e.getException();
 201         }
 202         return new InputStreamEnumeration(urls);
 203     }
 204 
 205 
 206     /**
 207      * Package private.
 208      * <p>
 209      * This internal method returns Thread Context Class Loader (TCCL),
 210      * if null, returns the system Class Loader.
 211      * <p>
 212      * Please don't expose this method as public.
 213      * @throws SecurityException if the class loader is not accessible
 214      */
 215     ClassLoader getContextClassLoader() {
 216 
 217         PrivilegedAction<ClassLoader> act = () -> {
 218             ClassLoader loader = Thread.currentThread().getContextClassLoader();
 219             if (loader == null) {
 220                 // Don't use bootstrap class loader directly!
 221                 loader = ClassLoader.getSystemClassLoader();
 222             }
 223             return loader;
 224         };
 225         return AccessController.doPrivileged(act);
 226     }
 227 
 228     private static URL[] getUrlArray(String codebase)
 229             throws MalformedURLException {
 230         // Parse codebase into separate URLs
 231         StringTokenizer parser = new StringTokenizer(codebase);
 232         List<String> list = new ArrayList<>();
 233         while (parser.hasMoreTokens()) {
 234             list.add(parser.nextToken());
 235         }
 236         String[] url = new String[list.size()];
 237         for (int i = 0; i < url.length; i++) {
 238             url[i] = list.get(i);
 239         }
 240 
 241         URL[] urlArray = new URL[url.length];
 242         for (int i = 0; i < urlArray.length; i++) {
 243             urlArray[i] = new URL(url[i]);
 244         }
 245         return urlArray;
 246     }
 247 
 248     /**
 249      * Given an enumeration of URLs, an instance of this class represents
 250      * an enumeration of their InputStreams.  Each operation on the URL
 251      * enumeration is performed within a doPrivileged block.
 252      * This is used to enumerate the resources under a foreign codebase.
 253      * This class is not MT-safe.
 254      */
 255     private class InputStreamEnumeration implements
 256             NamingEnumeration<InputStream> {
 257 
 258         private final Enumeration<URL> urls;
 259 
 260         private InputStream nextElement;
 261 
 262         InputStreamEnumeration(Enumeration<URL> urls) {
 263             this.urls = urls;
 264         }
 265 
 266         /*
 267          * Returns the next InputStream, or null if there are no more.
 268          * An InputStream that cannot be opened is skipped.
 269          */
 270         private InputStream getNextElement() {
 271             PrivilegedAction<InputStream> act = () -> {
 272                 while (urls.hasMoreElements()) {
 273                     try {
 274                         return urls.nextElement().openStream();
 275                     } catch (IOException e) {
 276                         // skip this URL
 277                     }
 278                 }
 279                 return null;
 280             };
 281             return AccessController.doPrivileged(act);
 282         }
 283 
 284         public boolean hasMore() {
 285             if (nextElement != null) {
 286                 return true;
 287             }
 288             nextElement = getNextElement();
 289             return (nextElement != null);
 290         }
 291 
 292         public boolean hasMoreElements() {
 293             return hasMore();
 294         }
 295 
 296         public InputStream next() {
 297             if (hasMore()) {
 298                 InputStream res = nextElement;
 299                 nextElement = null;
 300                 return res;
 301             } else {
 302                 throw new NoSuchElementException();
 303             }
 304         }
 305 
 306         public InputStream nextElement() {
 307             return next();
 308         }
 309 
 310         public void close() {
 311         }
 312     }
 313 }