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 }