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 Class<?> c = getFactoryClass(factoryClassName, null, false); 140 factory = (RowSetFactory) c.newInstance(); 141 } 142 } catch (Exception e) { 143 throw new SQLException( "RowSetFactory: " + factoryClassName + 144 " could not be instantiated: ", e); 145 } 146 147 // Check to see if we found the RowSetFactory via a System property 148 if (factory == null) { 149 // If the RowSetFactory is not found via a System Property, now 150 // look it up via the ServiceLoader API and if not found, use the 151 // Java SE default. 152 factory = loadViaServiceLoader(); 153 } 154 return factory == null ? defaultRowSetFactory() : factory; 155 } 156 157 private static RowSetFactory defaultRowSetFactory() { 158 return new com.sun.rowset.RowSetFactoryImpl(); 159 } 160 161 /** 162 * <p>Creates a new instance of a <code>RowSetFactory</code> from the 163 * specified factory class name. 164 * This function is useful when there are multiple providers in the classpath. 165 * It gives more control to the application as it can specify which provider 166 * should be loaded.</p> 167 * 168 * <p>Once an application has obtained a reference to a <code>RowSetFactory</code> 169 * it can use the factory to obtain RowSet instances.</p> 170 * 171 * @param factoryClassName fully qualified factory class name that 172 * provides an implementation of <code>javax.sql.rowset.RowSetFactory</code>. 173 * 174 * @param cl <code>ClassLoader</code> used to load the factory 175 * class. If <code>null</code> current <code>Thread</code>'s context 176 * classLoader is used to load the factory class. 177 * 178 * @return New instance of a <code>RowSetFactory</code> 179 * 180 * @throws SQLException if <code>factoryClassName</code> is 181 * <code>null</code>, or the factory class cannot be loaded, instantiated. 182 * 183 * @see #newFactory() 184 * 185 * @since 1.7 186 */ 187 public static RowSetFactory newFactory(String factoryClassName, ClassLoader cl) 188 throws SQLException { 189 190 trace("***In newInstance()"); 191 192 if(factoryClassName == null) { 193 throw new SQLException("Error: factoryClassName cannot be null"); 194 } 195 try { 196 ReflectUtil.checkPackageAccess(factoryClassName); 197 } catch (java.security.AccessControlException e) { 198 throw new SQLException("Access Exception",e); 199 } 200 201 try { 202 // getFactoryClass takes care of adding the read edge if 203 // necessary 204 Class<?> providerClass = getFactoryClass(factoryClassName, cl, false); 205 RowSetFactory instance = (RowSetFactory) providerClass.newInstance(); 206 if (debug) { 207 trace("Created new instance of " + providerClass + 208 " using ClassLoader: " + cl); 209 } 210 return instance; 211 } catch (ClassNotFoundException x) { 212 throw new SQLException( 213 "Provider " + factoryClassName + " not found", x); 214 } catch (Exception x) { 215 throw new SQLException( 216 "Provider " + factoryClassName + " could not be instantiated: " + x, 217 x); 218 } 219 } 220 221 /* 222 * Returns the class loader to be used. 223 * @return The ClassLoader to use. 224 * 225 */ 226 static private ClassLoader getContextClassLoader() throws SecurityException { 227 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 228 229 public ClassLoader run() { 230 ClassLoader cl = null; 231 232 cl = Thread.currentThread().getContextClassLoader(); 233 234 if (cl == null) { 235 cl = ClassLoader.getSystemClassLoader(); 236 } 237 238 return cl; 239 } 240 }); 241 } 242 243 /** 244 * Attempt to load a class using the class loader supplied. If that fails 245 * and fall back is enabled, the current (i.e. bootstrap) class loader is 246 * tried. 247 * 248 * If the class loader supplied is <code>null</code>, first try using the 249 * context class loader followed by the current class loader. 250 * @return The class which was loaded 251 */ 252 static private Class<?> getFactoryClass(String factoryClassName, ClassLoader cl, 253 boolean doFallback) throws ClassNotFoundException { 254 Class<?> factoryClass = null; 255 256 try { 257 if (cl == null) { 258 cl = getContextClassLoader(); 259 if (cl == null) { 260 throw new ClassNotFoundException(); 261 } else { 262 factoryClass = cl.loadClass(factoryClassName); 263 } 264 } else { 265 factoryClass = cl.loadClass(factoryClassName); 266 } 267 } catch (ClassNotFoundException e) { 268 if (doFallback) { 269 // Use current class loader 270 factoryClass = Class.forName(factoryClassName, true, RowSetFactory.class.getClassLoader()); 271 } else { 272 throw e; 273 } 274 } 275 276 ReflectUtil.checkPackageAccess(factoryClass); 277 return factoryClass; 278 } 279 280 /** 281 * Use the ServiceLoader mechanism to load the default RowSetFactory 282 * @return default RowSetFactory Implementation 283 */ 284 static private RowSetFactory loadViaServiceLoader() throws SQLException { 285 RowSetFactory theFactory = null; 286 try { 287 trace("***in loadViaServiceLoader():"); 288 for (RowSetFactory factory : ServiceLoader.load(javax.sql.rowset.RowSetFactory.class)) { 289 trace(" Loading done by the java.util.ServiceLoader :" + factory.getClass().getName()); 290 theFactory = factory; 291 break; 292 } 293 } catch (ServiceConfigurationError e) { 294 throw new SQLException( 295 "RowSetFactory: Error locating RowSetFactory using Service " 296 + "Loader API: " + e, e); 297 } 298 return theFactory; 299 300 } 301 302 /** 303 * Returns the requested System Property. If a {@code SecurityException} 304 * occurs, just return NULL 305 * @param propName - System property to retrieve 306 * @return The System property value or NULL if the property does not exist 307 * or a {@code SecurityException} occurs. 308 */ 309 static private String getSystemProperty(final String propName) { 310 String property = null; 311 try { 312 property = AccessController.doPrivileged(new PrivilegedAction<String>() { 313 314 public String run() { 315 return System.getProperty(propName); 316 } 317 }, null, new PropertyPermission(propName, "read")); 318 } catch (SecurityException se) { 319 trace("error getting " + propName + ": "+ se); 320 if (debug) { 321 se.printStackTrace(); 322 } 323 } 324 return property; 325 } 326 327 /** 328 * Debug routine which will output tracing if the System Property 329 * -Djavax.sql.rowset.RowSetFactory.debug is set 330 * @param msg - The debug message to display 331 */ 332 private static void trace(String msg) { 333 if (debug) { 334 System.err.println("###RowSets: " + msg); 335 } 336 } 337 }