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