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 26package javax.sql.rowset; 27 28import java.security.AccessController; 29import java.security.PrivilegedAction; 30import java.sql.SQLException; 31import java.util.PropertyPermission; 32import java.util.ServiceConfigurationError; 33import java.util.ServiceLoader; 34import 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 */ 58public 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} 340