/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.sql.rowset;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.sql.
SQLException;
import java.util.
PropertyPermission;
import java.util.
ServiceConfigurationError;
import java.util.
ServiceLoader;
import javax.sql.rowset.spi.
SyncFactoryException;
import sun.reflect.misc.
ReflectUtil;
/**
* A factory API that enables applications to obtain a
* {@code RowSetFactory} implementation that can be used to create different
* types of {@code RowSet} implementations.
* <p>
* Example:
* </p>
* <pre>
* RowSetFactory aFactory = RowSetProvider.newFactory();
* CachedRowSet crs = aFactory.createCachedRowSet();
* ...
* RowSetFactory rsf = RowSetProvider.newFactory("com.sun.rowset.RowSetFactoryImpl", null);
* WebRowSet wrs = rsf.createWebRowSet();
* </pre>
*<p>
* Tracing of this class may be enabled by setting the System property
* {@code javax.sql.rowset.RowSetFactory.debug} to any value but {@code false}.
* </p>
*
* @author Lance Andersen
* @since 1.7
*/
public class
RowSetProvider {
private static final
String ROWSET_DEBUG_PROPERTY = "javax.sql.rowset.RowSetProvider.debug";
private static final
String ROWSET_FACTORY_IMPL = "com.sun.rowset.RowSetFactoryImpl";
private static final
String ROWSET_FACTORY_NAME = "javax.sql.rowset.RowSetFactory";
/**
* Internal debug flag.
*/
private static boolean
debug = true;
static {
// Check to see if the debug property is set
String val =
getSystemProperty(
ROWSET_DEBUG_PROPERTY);
// Allow simply setting the prop to turn on debug
debug =
val != null && !"false".
equals(
val);
}
/**
* RowSetProvider constructor
*/
protected
RowSetProvider () {
}
/**
* <p>Creates a new instance of a <code>RowSetFactory</code>
* implementation. This method uses the following
* look up order to determine
* the <code>RowSetFactory</code> implementation class to load:</p>
* <ul>
* <li>
* The System property {@code javax.sql.rowset.RowSetFactory}. For example:
* <ul>
* <li>
* -Djavax.sql.rowset.RowSetFactory=com.sun.rowset.RowSetFactoryImpl
* </li>
* </ul>
* <li>
* The {@link ServiceLoader} API. The {@code ServiceLoader} API will look
* for a class name in the file
* {@code META-INF/services/javax.sql.rowset.RowSetFactory}
* in jars available to the runtime. For example, to have the the RowSetFactory
* implementation {@code com.sun.rowset.RowSetFactoryImpl } loaded, the
* entry in {@code META-INF/services/javax.sql.rowset.RowSetFactory} would be:
* <ul>
* <li>
* {@code com.sun.rowset.RowSetFactoryImpl }
* </li>
* </ul>
* </li>
* <li>
* Platform default <code>RowSetFactory</code> instance.
* </li>
* </ul>
*
* <p>Once an application has obtained a reference to a {@code RowSetFactory},
* it can use the factory to obtain RowSet instances.</p>
*
* @return New instance of a <code>RowSetFactory</code>
*
* @throws SQLException if the default factory class cannot be loaded,
* instantiated. The cause will be set to actual Exception
*
* @see ServiceLoader
* @since 1.7
*/
public static
RowSetFactory newFactory()
throws
SQLException {
// Use the system property first
RowSetFactory factory = null;
String factoryClassName = null;
try {
trace("Checking for Rowset System Property...");
factoryClassName =
getSystemProperty(
ROWSET_FACTORY_NAME);
if (
factoryClassName != null) {
trace("Found system property, value=" +
factoryClassName);
factory = (
RowSetFactory)
ReflectUtil.
newInstance(
getFactoryClass(
factoryClassName, null, true));
}
} catch (
Exception e) {
throw new
SQLException( "RowSetFactory: " +
factoryClassName +
" could not be instantiated: ",
e);
}
// Check to see if we found the RowSetFactory via a System property
if (
factory == null) {
// If the RowSetFactory is not found via a System Property, now
// look it up via the ServiceLoader API and if not found, use the
// Java SE default.
factory =
loadViaServiceLoader();
factory =
factory == null ?
newFactory(
ROWSET_FACTORY_IMPL, null) :
factory;
}
return (
factory);
}
/**
* <p>Creates a new instance of a <code>RowSetFactory</code> from the
* specified factory class name.
* This function is useful when there are multiple providers in the classpath.
* It gives more control to the application as it can specify which provider
* should be loaded.</p>
*
* <p>Once an application has obtained a reference to a <code>RowSetFactory</code>
* it can use the factory to obtain RowSet instances.</p>
*
* @param factoryClassName fully qualified factory class name that
* provides an implementation of <code>javax.sql.rowset.RowSetFactory</code>.
*
* @param cl <code>ClassLoader</code> used to load the factory
* class. If <code>null</code> current <code>Thread</code>'s context
* classLoader is used to load the factory class.
*
* @return New instance of a <code>RowSetFactory</code>
*
* @throws SQLException if <code>factoryClassName</code> is
* <code>null</code>, or the factory class cannot be loaded, instantiated.
*
* @see #newFactory()
*
* @since 1.7
*/
public static
RowSetFactory newFactory(
String factoryClassName,
ClassLoader cl)
throws
SQLException {
trace("***In newInstance()");
if(
factoryClassName == null) {
throw new
SQLException("Error: factoryClassName cannot be null");
}
try {
ReflectUtil.
checkPackageAccess(
factoryClassName);
} catch (java.security.
AccessControlException e) {
throw new
SQLException("Access Exception",
e);
}
try {
Class<?>
providerClass =
getFactoryClass(
factoryClassName,
cl, false);
RowSetFactory instance = (
RowSetFactory)
providerClass.
newInstance();
if (
debug) {
trace("Created new instance of " +
providerClass +
" using ClassLoader: " +
cl);
}
return
instance;
} catch (
ClassNotFoundException x) {
throw new
SQLException(
"Provider " +
factoryClassName + " not found",
x);
} catch (
Exception x) {
throw new
SQLException(
"Provider " +
factoryClassName + " could not be instantiated: " +
x,
x);
}
}
/*
* Returns the class loader to be used.
* @return The ClassLoader to use.
*
*/
static private
ClassLoader getContextClassLoader() throws
SecurityException {
return
AccessController.
doPrivileged(new
PrivilegedAction<
ClassLoader>() {
public
ClassLoader run() {
ClassLoader cl = null;
cl =
Thread.
currentThread().
getContextClassLoader();
if (
cl == null) {
cl =
ClassLoader.
getSystemClassLoader();
}
return
cl;
}
});
}
/**
* Attempt to load a class using the class loader supplied. If that fails
* and fall back is enabled, the current (i.e. bootstrap) class loader is
* tried.
*
* If the class loader supplied is <code>null</code>, first try using the
* context class loader followed by the current class loader.
* @return The class which was loaded
*/
static private
Class<?>
getFactoryClass(
String factoryClassName,
ClassLoader cl,
boolean
doFallback) throws
ClassNotFoundException {
try {
if (
cl == null) {
cl =
getContextClassLoader();
if (
cl == null) {
throw new
ClassNotFoundException();
} else {
return
cl.
loadClass(
factoryClassName);
}
} else {
return
cl.
loadClass(
factoryClassName);
}
} catch (
ClassNotFoundException e) {
if (
doFallback) {
// Use current class loader
return
Class.
forName(
factoryClassName, true,
RowSetFactory.class.
getClassLoader());
} else {
throw
e;
}
}
}
/**
* Use the ServiceLoader mechanism to load the default RowSetFactory
* @return default RowSetFactory Implementation
*/
static private
RowSetFactory loadViaServiceLoader() throws
SQLException {
RowSetFactory theFactory = null;
try {
trace("***in loadViaServiceLoader():");
for (
RowSetFactory factory :
ServiceLoader.
load(javax.sql.rowset.
RowSetFactory.class)) {
trace(" Loading done by the java.util.ServiceLoader :" +
factory.
getClass().
getName());
theFactory =
factory;
break;
}
} catch (
ServiceConfigurationError e) {
throw new
SQLException(
"RowSetFactory: Error locating RowSetFactory using Service "
+ "Loader API: " +
e,
e);
}
return
theFactory;
}
/**
* Returns the requested System Property. If a {@code SecurityException}
* occurs, just return NULL
* @param propName - System property to retrieve
* @return The System property value or NULL if the property does not exist
* or a {@code SecurityException} occurs.
*/
static private
String getSystemProperty(final
String propName) {
String property = null;
try {
property =
AccessController.
doPrivileged(new
PrivilegedAction<
String>() {
public
String run() {
return
System.
getProperty(
propName);
}
}, null, new
PropertyPermission(
propName, "read"));
} catch (
SecurityException se) {
trace("error getting " +
propName + ": "+
se);
if (
debug) {
se.
printStackTrace();
}
}
return
property;
}
/**
* Debug routine which will output tracing if the System Property
* -Djavax.sql.rowset.RowSetFactory.debug is set
* @param msg - The debug message to display
*/
private static void
trace(
String msg) {
if (
debug) {
System.
err.
println("###RowSets: " +
msg);
}
}
}