The Service Provider Interface (SPI) mechanism that generates SyncProvider
instances to be used by disconnected RowSet objects.
The SyncProvider instances in turn provide the
javax.sql.RowSetReader object the RowSet object
needs to populate itself with data and the
javax.sql.RowSetWriter object it needs to
propagate changes to its
data back to the underlying data source.
Because the methods in the SyncFactory class are all static,
there is only one SyncFactory object
per Java VM at any one time. This ensures that there is a single source from which a
RowSet implementation can obtain its SyncProvider
implementation.
1.0 Overview
The SyncFactory class provides an internal registry of available
synchronization provider implementations (SyncProvider objects).
This registry may be queried to determine which
synchronization providers are available.
The following line of code gets an enumeration of the providers currently registered.
java.util.Enumeration e = SyncFactory.getRegisteredProviders();
All standard RowSet implementations must provide at least two providers:
- an optimistic provider for use with a
CachedRowSet implementation
or an implementation derived from it
- an XML provider, which is used for reading and writing XML, such as with
WebRowSet objects
Note that the JDBC RowSet Implementations include the SyncProvider
implemtations RIOptimisticProvider and RIXmlProvider ,
which satisfy this requirement.
The SyncFactory class provides accessor methods to assist
applications in determining which synchronization providers are currently
registered with the SyncFactory .
Other methods let RowSet persistence providers be
registered or de-registered with the factory mechanism. This
allows additional synchronization provider implementations to be made
available to RowSet objects at run time.
Applications can apply a degree of filtering to determine the level of
synchronization that a SyncProvider implementation offers.
The following criteria determine whether a provider is
made available to a RowSet object:
- If a particular provider is specified by a
RowSet object, and
the SyncFactory does not contain a reference to this provider,
a SyncFactoryException is thrown stating that the synchronization
provider could not be found.
- If a
RowSet implementation is instantiated with a specified
provider and the specified provider has been properly registered, the
requested provider is supplied. Otherwise a SyncFactoryException
is thrown.
- If a
RowSet object does not specify a
SyncProvider implementation and no additional
SyncProvider implementations are available, the reference
implementation providers are supplied.
2.0 Registering SyncProvider Implementations
Both vendors and developers can register SyncProvider
implementations using one of the following mechanisms.
- Using the command line
The name of the provider is supplied on the command line, which will add
the provider to the system properties.
For example:
-Drowset.provider.classname=com.fred.providers.HighAvailabilityProvider
- Using the Standard Properties File
The reference implementation is targeted
to ship with J2SE 1.5, which will include an additional resource file
that may be edited by hand. Here is an example of the properties file
included in the reference implementation:
#Default JDBC RowSet sync providers listing
#
# Optimistic synchronization provider
rowset.provider.classname.0=com.sun.rowset.providers.RIOptimisticProvider
rowset.provider.vendor.0=Sun Microsystems Inc
rowset.provider.version.0=1.0
# XML Provider using standard XML schema
rowset.provider.classname.1=com.sun.rowset.providers.RIXMLProvider
rowset.provider.vendor.1=Sun Microsystems Inc.
rowset.provider.version.1=1.0
The SyncFactory checks this file and registers the
SyncProvider implementations that it contains. A
developer or vendor can add other implementations to this file.
For example, here is a possible addition:
rowset.provider.classname.2=com.fred.providers.HighAvailabilityProvider
rowset.provider.vendor.2=Fred, Inc.
rowset.provider.version.2=1.0
- Using a JNDI Context
Available providers can be registered on a JNDI
context, and the SyncFactory will attempt to load
SyncProvider implementations from that JNDI context.
For example, the following code fragment registers a provider implementation
on a JNDI context. This is something a deployer would normally do. In this
example, MyProvider is being registered on a CosNaming
namespace, which is the namespace used by J2EE resources.
import javax.naming.*;
Hashtable svrEnv = new Hashtable();
srvEnv.put(Context.INITIAL_CONTEXT_FACTORY, "CosNaming");
Context ctx = new InitialContext(svrEnv);
com.fred.providers.MyProvider = new MyProvider();
ctx.rebind("providers/MyProvider", syncProvider);
Next, an application will register the JNDI context with the
SyncFactory instance. This allows the SyncFactory
to browse within the JNDI context looking for SyncProvider
implementations.
Hashtable appEnv = new Hashtable();
appEnv.put(Context.INITIAL_CONTEXT_FACTORY, "CosNaming");
appEnv.put(Context.PROVIDER_URL, "iiop://hostname/providers");
Context ctx = new InitialContext(appEnv);
SyncFactory.registerJNDIContext(ctx);
If a RowSet object attempts to obtain a MyProvider
object, the SyncFactory will try to locate it. First it searches
for it in the system properties, then it looks in the resource files, and
finally it checks the JNDI context that has been set. The SyncFactory
instance verifies that the requested provider is a valid extension of the
SyncProvider abstract class and then gives it to the
RowSet object. In the following code fragment, a new
CachedRowSet object is created and initialized with
env, which contains the binding to MyProvider .
Hashtable env = new Hashtable();
env.put(SyncFactory.ROWSET_SYNC_PROVIDER, "com.fred.providers.MyProvider");
CachedRowSet crs = new com.sun.rowset.CachedRowSetImpl(env);
Further details on these mechanisms are available in the
javax.sql.rowset.spi package specification. |
Fields Summary |
---|
private static SyncFactory | syncFactory |
public static String | ROWSET_SYNC_PROVIDERThe standard property-id for a synchronization provider implementation
name. |
public static String | ROWSET_SYNC_VENDORThe standard property-id for a synchronization provider implementation
vendor name. |
public static String | ROWSET_SYNC_PROVIDER_VERSIONThe standard property-id for a synchronization provider implementation
version tag. |
private static String | ROWSET_PROPERTIESThe standard resource file name. |
private static String | default_providerThe RI Optimistic Provider. |
private static Context | icThe initial JNDI context where SyncProvider implementations can
be stored and from which they can be invoked. |
private static Logger | rsLoggerThe Logger object to be used by the SyncFactory . |
private static Level | rsLevel |
private static Hashtable | implementationsThe registry of available SyncProvider implementations.
See section 2.0 of the class comment for SyncFactory for an
explanation of how a provider can be added to this registry. |
private static Object | logSyncInternal sync object used to maintain the SPI as a singleton |
private static PrintWriter | logWriterInternal PrintWriter field for logging facility |
private static String | colon |
private static String | strFileSep |
private static boolean | jndiCtxEstablishedThe internal boolean switch that indicates whether a JNDI
context has been established or not. |
private static boolean | debugThe internal debug switch. |
private static int | providerImplIndexInternal registry count for the number of providers contained in the
registry. |
private static boolean | lazyJNDICtxRefreshInternal switch indicating whether the JNDI namespace should be re-read. |
Methods Summary |
---|
private static void | enumerateBindings(javax.naming.NamingEnumeration bindings, java.util.Properties properties)Scans each binding on JNDI context and determines if any binding is an
instance of SyncProvider, if so, add this to the registry and continue to
scan the current context using a re-entrant call to this method until all
bindings have been enumerated.
boolean syncProviderObj = false; // move to parameters ?
try {
Binding bd = null;
Object elementObj = null;
String element = null;
while (bindings.hasMore()) {
bd = (Binding)bindings.next();
element = bd.getName();
elementObj = bd.getObject();
if (!(ic.lookup(element) instanceof Context)) {
// skip directories/sub-contexts
if (ic.lookup(element) instanceof SyncProvider) {
syncProviderObj = true;
}
}
if (syncProviderObj) {
SyncProvider sync = (SyncProvider)elementObj;
properties.put(SyncFactory.ROWSET_SYNC_PROVIDER,
sync.getProviderID());
syncProviderObj = false; // reset
}
}
} catch (javax.naming.NotContextException e) {
bindings.next();
// Re-entrant call into method
enumerateBindings(bindings, properties);
}
|
public static javax.sql.rowset.spi.SyncProvider | getInstance(java.lang.String providerID)Returns the SyncProvider instance identified by providerID.
initMapIfNecessary(); // populate HashTable
initJNDIContext(); // check JNDI context for any additional bindings
ProviderImpl impl = (ProviderImpl)implementations.get(providerID);
if (impl == null) {
// Requested SyncProvider is unavailable. Return default provider.
return new com.sun.rowset.providers.RIOptimisticProvider();
}
// Attempt to invoke classname from registered SyncProvider list
Class c = null;
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
/**
* The SyncProvider implementation of the user will be in
* the classpath. We need to find the ClassLoader which loads
* this SyncFactory and try to laod the SyncProvider class from
* there.
**/
c = Class.forName(providerID, true, cl);
if (c != null) {
return (SyncProvider)c.newInstance();
} else {
return new com.sun.rowset.providers.RIOptimisticProvider();
}
} catch (IllegalAccessException e) {
throw new SyncFactoryException("IllegalAccessException: " + e.getMessage());
} catch (InstantiationException e) {
throw new SyncFactoryException("InstantiationException: " + e.getMessage());
} catch (ClassNotFoundException e) {
throw new SyncFactoryException("ClassNotFoundException: " + e.getMessage());
}
|
public static java.util.logging.Logger | getLogger()Returns the logging object for applications to retrieve
synchronization events posted by SyncProvider implementations.
// only one logger per session
if(rsLogger == null){
throw new SyncFactoryException("(SyncFactory) : No logger has been set");
}
return rsLogger;
|
private static java.lang.String[] | getPropertyNames(boolean append)Used by the parseProperties methods to disassemble each property tuple.
return getPropertyNames(append, null);
|
private static java.lang.String[] | getPropertyNames(boolean append, java.lang.String propertyIndex)Disassembles each property and its associated value. Also handles
overloaded property names that contain indexes.
String dot = ".";
String[] propertyNames =
new String[] {SyncFactory.ROWSET_SYNC_PROVIDER,
SyncFactory.ROWSET_SYNC_VENDOR,
SyncFactory.ROWSET_SYNC_PROVIDER_VERSION};
if (append) {
for (int i = 0; i < propertyNames.length; i++) {
propertyNames[i] = propertyNames[i] +
dot +
propertyIndex;
}
return propertyNames;
} else {
return propertyNames;
}
|
public static java.util.Enumeration | getRegisteredProviders()Returns an Enumeration of currently registered synchronization
providers. A RowSet implementation may use any provider in
the enumeration as its SyncProvider object.
At a minimum, the reference synchronization provider allowing
RowSet content data to be stored using a JDBC driver should be
possible.
initMapIfNecessary();
// return a collection of classnames
// of type SyncProvider
return implementations.elements();
|
public static javax.sql.rowset.spi.SyncFactory | getSyncFactory()Returns the SyncFactory singleton.
// This method uses the Singleton Design Pattern
// with Double-Checked Locking Pattern for
// 1. Creating single instance of the SyncFactory
// 2. Make the class thread safe, so that at one time
// only one thread enters the synchronized block
// to instantiate.
// if syncFactory object is already there
// don't go into synchronized block and return
// that object.
// else go into synchronized block
if(syncFactory == null){
synchronized(SyncFactory.class) {
if(syncFactory == null){
syncFactory = new SyncFactory();
} //end if
} //end synchronized block
} //end if
return syncFactory;
|
private static void | initJNDIContext()Controls JNDI context intialization.
if (jndiCtxEstablished && (ic != null) && (lazyJNDICtxRefresh == false)) {
try {
parseProperties(parseJNDIContext());
lazyJNDICtxRefresh = true; // touch JNDI namespace once.
} catch (NamingException e) {
e.printStackTrace();
throw new SyncFactoryException("SPI: NamingException: " + e.getExplanation());
} catch (Exception e) {
e.printStackTrace();
throw new SyncFactoryException("SPI: Exception: " + e.getMessage());
}
}
|
private static synchronized void | initMapIfNecessary()
// Local implementation class names and keys from Properties
// file, translate names into Class objects using Class.forName
// and store mappings
Properties properties = new Properties();
if (implementations == null) {
implementations = new Hashtable();
try {
// check if user is supplying his Synchronisation Provider
// Implementation if not use Sun's implementation.
// properties.load(new FileInputStream(ROWSET_PROPERTIES));
// The rowset.properties needs to be in jdk/jre/lib when
// integrated with jdk.
// else it should be picked from -D option from command line.
// -Drowset.properties will add to standard properties. Similar
// keys will over-write
/*
* Dependent on application
*/
String strRowsetProperties = System.getProperty("rowset.properties");
if ( strRowsetProperties != null) {
// Load user's implementation of SyncProvider
// here. -Drowset.properties=/abc/def/pqr.txt
ROWSET_PROPERTIES = strRowsetProperties;
properties.load(new FileInputStream(ROWSET_PROPERTIES));
parseProperties(properties);
}
/*
* Always available
*/
ROWSET_PROPERTIES = "javax" + strFileSep + "sql" +
strFileSep + "rowset" + strFileSep +
"rowset.properties";
// properties.load(
// ClassLoader.getSystemResourceAsStream(ROWSET_PROPERTIES));
ClassLoader cl = Thread.currentThread().getContextClassLoader();
properties.load(cl.getResourceAsStream(ROWSET_PROPERTIES));
parseProperties(properties);
// removed else, has properties should sum together
} catch (FileNotFoundException e) {
throw new SyncFactoryException("Cannot locate properties file: " + e);
} catch (IOException e) {
throw new SyncFactoryException("IOException: " + e);
}
/*
* Now deal with -Drowset.provider.classname
* load additional properties from -D command line
*/
properties.clear();
String providerImpls = System.getProperty(ROWSET_SYNC_PROVIDER);
if (providerImpls != null) {
int i = 0;
if (providerImpls.indexOf(colon) > 0) {
StringTokenizer tokenizer = new StringTokenizer(providerImpls, colon);
while (tokenizer.hasMoreElements()) {
properties.put(ROWSET_SYNC_PROVIDER + "." + i, tokenizer.nextToken());
i++;
}
} else {
properties.put(ROWSET_SYNC_PROVIDER, providerImpls);
}
parseProperties(properties);
}
}
|
private static java.util.Properties | parseJNDIContext()Parses the set JNDI Context and passes bindings to the enumerateBindings
method when complete.
NamingEnumeration bindings = ic.listBindings("");
Properties properties = new Properties();
// Hunt one level below context for available SyncProvider objects
enumerateBindings(bindings, properties);
return properties;
|
private static void | parseProperties(java.util.Properties p)Internal handler for all standard property parsing. Parses standard
ROWSET properties and stores lazy references into the the internal registry.
ProviderImpl impl = null;
String key = null;
String[] propertyNames = null;
for (Enumeration e = p.propertyNames(); e.hasMoreElements() ;) {
String str = (String)e.nextElement();
int w = str.length();
if (str.startsWith(SyncFactory.ROWSET_SYNC_PROVIDER)) {
impl = new ProviderImpl();
impl.setIndex(providerImplIndex++);
if (w == (SyncFactory.ROWSET_SYNC_PROVIDER).length()) {
// no property index has been set.
propertyNames = getPropertyNames(false);
} else {
// property index has been set.
propertyNames = getPropertyNames(true, str.substring(w-1));
}
key = p.getProperty(propertyNames[0]);
impl.setClassname(key);
impl.setVendor(p.getProperty(propertyNames[1]));
impl.setVersion(p.getProperty(propertyNames[2]));
implementations.put(key, impl);
}
}
|
public static synchronized void | registerProvider(java.lang.String providerID)Adds the the given synchronization provider to the factory register. Guidelines
are provided in the SyncProvider specification for the
required naming conventions for SyncProvider
implementations.
Synchronization providers bound to a JNDI context can be
registered by binding a SyncProvider instance to a JNDI namespace.
SyncProvider p = new MySyncProvider();
InitialContext ic = new InitialContext();
ic.bind ("jdbc/rowset/MySyncProvider", p);
Furthermore, an initial JNDI context should be set with the
SyncFactory using the setJNDIContext method.
The SyncFactory leverages this context to search for
available SyncProvider objects bound to the JNDI
context and its child nodes.
ProviderImpl impl = new ProviderImpl();
impl.setClassname(providerID);
initMapIfNecessary();
implementations.put(providerID, impl);
|
public static void | setJNDIContext(javax.naming.Context ctx)Sets the initial JNDI context from which SyncProvider implementations
can be retrieved from a JNDI namespace
if (ctx == null) {
throw new SyncFactoryException("Invalid JNDI context supplied");
}
ic = ctx;
jndiCtxEstablished = true;
|
public static void | setLogger(java.util.logging.Logger logger)Sets the logging object to be used by the SyncProvider
implementation provided by the SyncFactory . All
SyncProvider implementations can log their events to
this object and the application can retrieve a handle to this
object using the getLogger method.
rsLogger = logger;
|
public static void | setLogger(java.util.logging.Logger logger, java.util.logging.Level level)Sets the logging object that is used by SyncProvider
implementations provided by the SyncFactory SPI. All
SyncProvider implementations can log their events
to this object and the application can retrieve a handle to this
object using the getLogger method.
// singleton
rsLogger = logger;
rsLogger.setLevel(level);
|
private static void | showImpl(javax.sql.rowset.spi.ProviderImpl impl)Internal debug method that outputs the registry contents.
System.out.println("Provider implementation:");
System.out.println("Classname: " + impl.getClassname());
System.out.println("Vendor: " + impl.getVendor());
System.out.println("Version: " + impl.getVersion());
System.out.println("Impl index: " + impl.getIndex());
|
public static synchronized void | unregisterProvider(java.lang.String providerID)Removes the designated currently registered synchronization provider from the
Factory SPI register.
initMapIfNecessary();
if (implementations.containsKey(providerID)) {
implementations.remove(providerID);
}
|