FileDocCategorySizeDatePackage
SyncFactory.javaAPI DocJava SE 5 API32434Fri Aug 26 14:57:52 BST 2005javax.sql.rowset.spi

SyncFactory

public class SyncFactory extends Object
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:

  1. 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.

  2. 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.

  3. 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.
author
Jonathan Bruce
see
javax.sql.rowset.spi.SyncProvider
see
javax.sql.rowset.spi.SyncFactoryException

Fields Summary
private static SyncFactory
syncFactory
public static String
ROWSET_SYNC_PROVIDER
The standard property-id for a synchronization provider implementation name.
public static String
ROWSET_SYNC_VENDOR
The standard property-id for a synchronization provider implementation vendor name.
public static String
ROWSET_SYNC_PROVIDER_VERSION
The standard property-id for a synchronization provider implementation version tag.
private static String
ROWSET_PROPERTIES
The standard resource file name.
private static String
default_provider
The RI Optimistic Provider.
private static Context
ic
The initial JNDI context where SyncProvider implementations can be stored and from which they can be invoked.
private static Logger
rsLogger
The Logger object to be used by the SyncFactory.
private static Level
rsLevel
private static Hashtable
implementations
The 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
logSync
Internal sync object used to maintain the SPI as a singleton
private static PrintWriter
logWriter
Internal PrintWriter field for logging facility
private static String
colon
private static String
strFileSep
private static boolean
jndiCtxEstablished
The internal boolean switch that indicates whether a JNDI context has been established or not.
private static boolean
debug
The internal debug switch.
private static int
providerImplIndex
Internal registry count for the number of providers contained in the registry.
private static boolean
lazyJNDICtxRefresh
Internal switch indicating whether the JNDI namespace should be re-read.
Constructors Summary
private SyncFactory()
Creates a new SyncFactory object, which is the singleton instance. Having a private constructor guarantees that no more than one SyncProvider object can exist at a time.

 
                                    
      
Methods Summary
private static voidenumerateBindings(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.SyncProvidergetInstance(java.lang.String providerID)
Returns the SyncProvider instance identified by providerID.

param
providerID the unique identifier of the provider
return
a SyncProvider implementation
throws
SyncFactoryException If the SyncProvider cannot be found or some error was encountered when trying to invoke this provider.

        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.LoggergetLogger()
Returns the logging object for applications to retrieve synchronization events posted by SyncProvider implementations.

throws
SyncFactoryException if no logging object has been set.

	// 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.EnumerationgetRegisteredProviders()
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.

return
Enumeration A enumeration of available synchronization providers that are registered with this Factory

        initMapIfNecessary();
        // return a collection of classnames
        // of type SyncProvider
        return implementations.elements();
    
public static javax.sql.rowset.spi.SyncFactorygetSyncFactory()
Returns the SyncFactory singleton.

return
the SyncFactory instance

         
         // 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 voidinitJNDIContext()
Controls JNDI context intialization.

throws
SyncFactoryException if an error occurs parsing the JNDI context

        
                        
        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 voidinitMapIfNecessary()

    
    
                           
        
        // 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.PropertiesparseJNDIContext()
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 voidparseProperties(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 voidregisterProvider(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.

param
providerID A String object with the unique ID of the synchronization provider being registered
throws
SyncFactoryException if an attempt is made to supply an empty or null provider name
see
#setJNDIContext


                                                                                                                                                                               
          
          
        
        ProviderImpl impl = new ProviderImpl();
        impl.setClassname(providerID);
        initMapIfNecessary();
        implementations.put(providerID, impl);
        
    
public static voidsetJNDIContext(javax.naming.Context ctx)
Sets the initial JNDI context from which SyncProvider implementations can be retrieved from a JNDI namespace

param
ctx a valid JNDI context
throws
SyncFactoryException if the supplied JNDI context is null

	if (ctx == null) {
	    throw new SyncFactoryException("Invalid JNDI context supplied");
	}
	ic = ctx;        
        jndiCtxEstablished = true;
   
public static voidsetLogger(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.

param
logger A Logger object instance

        rsLogger = logger;
    
public static voidsetLogger(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.

param
logger a Logger object instance
param
level a Level object instance indicating the degree of logging required

	// singleton 
       
	rsLogger = logger;
	rsLogger.setLevel(level);
    
private static voidshowImpl(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 voidunregisterProvider(java.lang.String providerID)
Removes the designated currently registered synchronization provider from the Factory SPI register.

param
providerID The unique-id of the synchronization provider
throws
SyncFactoryException If an attempt is made to unregister a SyncProvider implementation that was not registered.

           
        initMapIfNecessary();    
        if (implementations.containsKey(providerID)) {
            implementations.remove(providerID);
        }