1 /*
   2  * Copyright (c) 2010, 2013, 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 javax.sql.rowset;
  27 
  28 import java.security.AccessController;
  29 import java.security.PrivilegedAction;
  30 import java.sql.SQLException;
  31 import java.util.PropertyPermission;
  32 import java.util.ServiceConfigurationError;
  33 import java.util.ServiceLoader;
  34 import sun.reflect.misc.ReflectUtil;
  35 
  36 /**
  37  * A factory API that enables applications to obtain a
  38  * {@code RowSetFactory} implementation  that can be used to create different
  39  * types of {@code RowSet} implementations.
  40  * <p>
  41  * Example:
  42  * </p>
  43  * <pre>
  44  * RowSetFactory aFactory = RowSetProvider.newFactory();
  45  * CachedRowSet crs = aFactory.createCachedRowSet();
  46  * ...
  47  * RowSetFactory rsf = RowSetProvider.newFactory("com.sun.rowset.RowSetFactoryImpl", null);
  48  * WebRowSet wrs = rsf.createWebRowSet();
  49  * </pre>
  50  *<p>
  51  * Tracing of this class may be enabled by setting the System property
  52  * {@code javax.sql.rowset.RowSetFactory.debug} to any value but {@code false}.
  53  * </p>
  54  *
  55  * @author Lance Andersen
  56  * @since 1.7
  57  */
  58 public class RowSetProvider {
  59 
  60     private static final String ROWSET_DEBUG_PROPERTY = "javax.sql.rowset.RowSetProvider.debug";
  61     private static final String ROWSET_FACTORY_IMPL = "com.sun.rowset.RowSetFactoryImpl";
  62     private static final String ROWSET_FACTORY_NAME = "javax.sql.rowset.RowSetFactory";
  63     /**
  64      * Internal debug flag.
  65      */
  66     private static boolean debug = true;
  67 
  68 
  69     static {
  70         // Check to see if the debug property is set
  71         String val = getSystemProperty(ROWSET_DEBUG_PROPERTY);
  72         // Allow simply setting the prop to turn on debug
  73         debug = val != null && !"false".equals(val);
  74     }
  75 
  76     /**
  77      * RowSetProvider constructor
  78      */
  79     protected RowSetProvider () {
  80     }
  81 
  82     /**
  83      * <p>Creates a new instance of a <code>RowSetFactory</code>
  84      * implementation.  This method uses the following
  85      * look up order to determine
  86      * the <code>RowSetFactory</code> implementation class to load:</p>
  87      * <ul>
  88      * <li>
  89      * The System property {@code javax.sql.rowset.RowSetFactory}.  For example:
  90      * <ul>
  91      * <li>
  92      * -Djavax.sql.rowset.RowSetFactory=com.sun.rowset.RowSetFactoryImpl
  93      * </li>
  94      * </ul>
  95      * <li>
  96      * The {@link ServiceLoader} API. The {@code ServiceLoader} API will look
  97      * for a class name in the file
  98      * {@code META-INF/services/javax.sql.rowset.RowSetFactory}
  99      * in jars available to the runtime. For example, to have the RowSetFactory
 100      * implementation {@code com.sun.rowset.RowSetFactoryImpl } loaded, the
 101      * entry in {@code META-INF/services/javax.sql.rowset.RowSetFactory} would be:
 102      *  <ul>
 103      * <li>
 104      * {@code com.sun.rowset.RowSetFactoryImpl }
 105      * </li>
 106      * </ul>
 107      * </li>
 108      * <li>
 109      * Platform default <code>RowSetFactory</code> instance.
 110      * </li>
 111      * </ul>
 112      *
 113      * <p>Once an application has obtained a reference to a {@code RowSetFactory},
 114      * it can use the factory to obtain RowSet instances.</p>
 115      *
 116      * @return New instance of a <code>RowSetFactory</code>
 117      *
 118      * @throws SQLException if the default factory class cannot be loaded,
 119      * instantiated. The cause will be set to actual Exception
 120      *
 121      * @see ServiceLoader
 122      * @since 1.7
 123      */
 124     public static RowSetFactory newFactory()
 125             throws SQLException {
 126         // Use the system property first
 127         RowSetFactory factory = null;
 128         String factoryClassName = null;
 129         try {
 130             trace("Checking for Rowset System Property...");
 131             factoryClassName = getSystemProperty(ROWSET_FACTORY_NAME);
 132             if (factoryClassName != null) {
 133                 trace("Found system property, value=" + factoryClassName);
 134                 if (factoryClassName.equals(ROWSET_FACTORY_IMPL)) {
 135                     return defaultRowSetFactory();
 136                 }
 137                 // getFactoryClass takes care of adding the read edge if
 138                 // necessary
 139                 @SuppressWarnings("deprecation")
 140                 Object o = getFactoryClass(factoryClassName, null, false).newInstance();
 141                 factory = (RowSetFactory) o;
 142             }
 143         } catch (Exception e) {
 144             throw new SQLException( "RowSetFactory: " + factoryClassName +
 145                     " could not be instantiated: ", e);
 146         }
 147 
 148         // Check to see if we found the RowSetFactory via a System property
 149         if (factory == null) {
 150             // If the RowSetFactory is not found via a System Property, now
 151             // look it up via the ServiceLoader API and if not found, use the
 152             // Java SE default.
 153             factory = loadViaServiceLoader();
 154         }
 155         return  factory == null ? defaultRowSetFactory() : factory;
 156     }
 157 
 158     private static RowSetFactory defaultRowSetFactory() {
 159         return new com.sun.rowset.RowSetFactoryImpl();
 160     }
 161 
 162     /**
 163      * <p>Creates  a new instance of a <code>RowSetFactory</code> from the
 164      * specified factory class name.
 165      * This function is useful when there are multiple providers in the classpath.
 166      * It gives more control to the application as it can specify which provider
 167      * should be loaded.</p>
 168      *
 169      * <p>Once an application has obtained a reference to a <code>RowSetFactory</code>
 170      * it can use the factory to obtain RowSet instances.</p>
 171      *
 172      * @param factoryClassName fully qualified factory class name that
 173      * provides  an implementation of <code>javax.sql.rowset.RowSetFactory</code>.
 174      *
 175      * @param cl <code>ClassLoader</code> used to load the factory
 176      * class. If <code>null</code> current <code>Thread</code>'s context
 177      * classLoader is used to load the factory class.
 178      *
 179      * @return New instance of a <code>RowSetFactory</code>
 180      *
 181      * @throws SQLException if <code>factoryClassName</code> is
 182      * <code>null</code>, or the factory class cannot be loaded, instantiated.
 183      *
 184      * @see #newFactory()
 185      *
 186      * @since 1.7
 187      */
 188     public static RowSetFactory newFactory(String factoryClassName, ClassLoader cl)
 189             throws SQLException {
 190 
 191         trace("***In newInstance()");
 192 
 193         if(factoryClassName == null) {
 194             throw new SQLException("Error: factoryClassName cannot be null");
 195         }
 196         try {
 197             ReflectUtil.checkPackageAccess(factoryClassName);
 198         } catch (java.security.AccessControlException e) {
 199             throw new SQLException("Access Exception",e);
 200         }
 201 
 202         try {
 203             // getFactoryClass takes care of adding the read edge if
 204             // necessary
 205             Class<?> providerClass = getFactoryClass(factoryClassName, cl, false);
 206             @SuppressWarnings("deprecation")
 207             RowSetFactory instance = (RowSetFactory) providerClass.newInstance();
 208             if (debug) {
 209                 trace("Created new instance of " + providerClass +
 210                         " using ClassLoader: " + cl);
 211             }
 212             return instance;
 213         } catch (ClassNotFoundException x) {
 214             throw new SQLException(
 215                     "Provider " + factoryClassName + " not found", x);
 216         } catch (Exception x) {
 217             throw new SQLException(
 218                     "Provider " + factoryClassName + " could not be instantiated: " + x,
 219                     x);
 220         }
 221     }
 222 
 223     /*
 224      * Returns the class loader to be used.
 225      * @return The ClassLoader to use.
 226      *
 227      */
 228     static private ClassLoader getContextClassLoader() throws SecurityException {
 229         return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
 230 
 231             public ClassLoader run() {
 232                 ClassLoader cl = null;
 233 
 234                 cl = Thread.currentThread().getContextClassLoader();
 235 
 236                 if (cl == null) {
 237                     cl = ClassLoader.getSystemClassLoader();
 238                 }
 239 
 240                 return cl;
 241             }
 242         });
 243     }
 244 
 245     /**
 246      * Attempt to load a class using the class loader supplied. If that fails
 247      * and fall back is enabled, the current (i.e. bootstrap) class loader is
 248      * tried.
 249      *
 250      * If the class loader supplied is <code>null</code>, first try using the
 251      * context class loader followed by the current class loader.
 252      *  @return The class which was loaded
 253      */
 254     static private Class<?> getFactoryClass(String factoryClassName, ClassLoader cl,
 255             boolean doFallback) throws ClassNotFoundException {
 256         Class<?> factoryClass = null;
 257 
 258         try {
 259             if (cl == null) {
 260                 cl = getContextClassLoader();
 261                 if (cl == null) {
 262                     throw new ClassNotFoundException();
 263                 } else {
 264                     factoryClass = cl.loadClass(factoryClassName);
 265                 }
 266             } else {
 267                 factoryClass = cl.loadClass(factoryClassName);
 268             }
 269         } catch (ClassNotFoundException e) {
 270             if (doFallback) {
 271                 // Use current class loader
 272                 factoryClass = Class.forName(factoryClassName, true, RowSetFactory.class.getClassLoader());
 273             } else {
 274                 throw e;
 275             }
 276         }
 277 
 278         ReflectUtil.checkPackageAccess(factoryClass);
 279         return factoryClass;
 280     }
 281 
 282     /**
 283      * Use the ServiceLoader mechanism to load  the default RowSetFactory
 284      * @return default RowSetFactory Implementation
 285      */
 286     static private RowSetFactory loadViaServiceLoader() throws SQLException {
 287         RowSetFactory theFactory = null;
 288         try {
 289             trace("***in loadViaServiceLoader():");
 290             for (RowSetFactory factory : ServiceLoader.load(javax.sql.rowset.RowSetFactory.class)) {
 291                 trace(" Loading done by the java.util.ServiceLoader :" + factory.getClass().getName());
 292                 theFactory = factory;
 293                 break;
 294             }
 295         } catch (ServiceConfigurationError e) {
 296             throw new SQLException(
 297                     "RowSetFactory: Error locating RowSetFactory using Service "
 298                     + "Loader API: " + e, e);
 299         }
 300         return theFactory;
 301 
 302     }
 303 
 304     /**
 305      * Returns the requested System Property.  If a {@code SecurityException}
 306      * occurs, just return NULL
 307      * @param propName - System property to retrieve
 308      * @return The System property value or NULL if the property does not exist
 309      * or a {@code SecurityException} occurs.
 310      */
 311     static private String getSystemProperty(final String propName) {
 312         String property = null;
 313         try {
 314             property = AccessController.doPrivileged(new PrivilegedAction<String>() {
 315 
 316                 public String run() {
 317                     return System.getProperty(propName);
 318                 }
 319             }, null, new PropertyPermission(propName, "read"));
 320         } catch (SecurityException se) {
 321             trace("error getting " + propName + ":  "+ se);
 322             if (debug) {
 323                 se.printStackTrace();
 324             }
 325         }
 326         return property;
 327     }
 328 
 329     /**
 330      * Debug routine which will output tracing if the System Property
 331      * -Djavax.sql.rowset.RowSetFactory.debug is set
 332      * @param msg - The debug message to display
 333      */
 334     private static void trace(String msg) {
 335         if (debug) {
 336             System.err.println("###RowSets: " + msg);
 337         }
 338     }
 339 }