934 lines · 33.9 KB · Java
RawDownload
/* * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.sql.rowset.spi; import java.util.logging.*;import java.util.*; import java.sql.*;import javax.sql.*; import java.io.FileInputStream;import java.io.InputStream;import java.io.IOException;import java.io.FileNotFoundException; import javax.naming.*; /** * The Service Provider Interface (SPI) mechanism that generates <code>SyncProvider</code> * instances to be used by disconnected <code>RowSet</code> objects. * The <code>SyncProvider</code> instances in turn provide the * <code>javax.sql.RowSetReader</code> object the <code>RowSet</code> object * needs to populate itself with data and the * <code>javax.sql.RowSetWriter</code> object it needs to * propagate changes to its * data back to the underlying data source. * <P> * Because the methods in the <code>SyncFactory</code> class are all static, * there is only one <code>SyncFactory</code> object * per Java VM at any one time. This ensures that there is a single source from which a * <code>RowSet</code> implementation can obtain its <code>SyncProvider</code> * implementation. * <p> * <h3>1.0 Overview</h3> * The <code>SyncFactory</code> class provides an internal registry of available * synchronization provider implementations (<code>SyncProvider</code> 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. * <PRE> *     java.util.Enumeration e = SyncFactory.getRegisteredProviders(); * </PRE> * All standard <code>RowSet</code> implementations must provide at least two providers: * <UL> *  <LI>an optimistic provider for use with a <code>CachedRowSet</code> implementation *     or an implementation derived from it *  <LI>an XML provider, which is used for reading and writing XML, such as with *       <code>WebRowSet</code> objects * </UL> * Note that the JDBC RowSet Implementations include the <code>SyncProvider</code> * implemtations <code>RIOptimisticProvider</code> and <code>RIXmlProvider</code>, * which satisfy this requirement. * <P> * The <code>SyncFactory</code> class provides accessor methods to assist * applications in determining which synchronization providers are currently * registered with the <code>SyncFactory</code>. * <p> * Other methods let <code>RowSet</code> persistence providers be * registered or de-registered with the factory mechanism. This * allows additional synchronization provider implementations to be made * available to <code>RowSet</code> objects at run time. * <p> * Applications can apply a degree of filtering to determine the level of * synchronization that a <code>SyncProvider</code> implementation offers. * The following criteria determine whether a provider is * made available to a <code>RowSet</code> object: * <ol> * <li>If a particular provider is specified by a <code>RowSet</code> object, and * the <code>SyncFactory</code> does not contain a reference to this provider, * a <code>SyncFactoryException</code> is thrown stating that the synchronization * provider could not be found. * <p> * <li>If a <code>RowSet</code> implementation is instantiated with a specified * provider and the specified provider has been properly registered, the * requested provider is supplied. Otherwise a <code>SyncFactoryException</code> * is thrown. * <p> * <li>If a <code>RowSet</code> object does not specify a * <code>SyncProvider</code> implementation and no additional * <code>SyncProvider</code> implementations are available, the reference * implementation providers are supplied. * </ol> * <h3>2.0 Registering <code>SyncProvider</code> Implementations</h3> * <p> * Both vendors and developers can register <code>SyncProvider</code> * implementations using one of the following mechanisms. * <ul> * <LI><B>Using the command line</B><BR> * The name of the provider is supplied on the command line, which will add * the provider to the system properties. * For example: * <PRE> *    -Drowset.provider.classname=com.fred.providers.HighAvailabilityProvider * </PRE> * <li><b>Using the Standard Properties File</b><BR> * 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: * <PRE> *   #Default JDBC RowSet sync providers listing *   # * *   # Optimistic synchronization provider *   rowset.provider.classname.0=com.sun.rowset.providers.RIOptimisticProvider *   rowset.provider.vendor.0=Oracle Corporation *   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=Oracle Corporation *   rowset.provider.version.1=1.0 * </PRE> * The <code>SyncFactory</code> checks this file and registers the * <code>SyncProvider</code> implementations that it contains. A * developer or vendor can add other implementations to this file. * For example, here is a possible addition: * <PRE> *     rowset.provider.classname.2=com.fred.providers.HighAvailabilityProvider *     rowset.provider.vendor.2=Fred, Inc. *     rowset.provider.version.2=1.0 * </PRE> * <p> * <li><b>Using a JNDI Context</b><BR> * Available providers can be registered on a JNDI * context, and the <code>SyncFactory</code> will attempt to load * <code>SyncProvider</code> 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, <code>MyProvider</code> is being registered on a CosNaming * namespace, which is the namespace used by J2EE resources. * <PRE> *    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); * </PRE> * </ul> * Next, an application will register the JNDI context with the * <code>SyncFactory</code> instance.  This allows the <code>SyncFactory</code> * to browse within the JNDI context looking for <code>SyncProvider</code> * implementations. * <PRE> *    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); * </PRE> * If a <code>RowSet</code> object attempts to obtain a <code>MyProvider</code> * object, the <code>SyncFactory</code> 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 <code>SyncFactory</code> * instance verifies that the requested provider is a valid extension of the * <code>SyncProvider</code> abstract class and then gives it to the * <code>RowSet</code> object. In the following code fragment, a new * <code>CachedRowSet</code> object is created and initialized with * <i>env</i>, which contains the binding to <code>MyProvider</code>. * <PRE> *    Hashtable env = new Hashtable(); *    env.put(SyncFactory.ROWSET_SYNC_PROVIDER, "com.fred.providers.MyProvider"); *    CachedRowSet crs = new com.sun.rowset.CachedRowSetImpl(env); * </PRE> * Further details on these mechanisms are available in the * <code>javax.sql.rowset.spi</code> package specification. * * @author  Jonathan Bruce * @see javax.sql.rowset.spi.SyncProvider * @see javax.sql.rowset.spi.SyncFactoryException */public class SyncFactory {     /**     * Creates a new <code>SyncFactory</code> object, which is the singleton     * instance.     * Having a private constructor guarantees that no more than     * one <code>SyncProvider</code> object can exist at a time.     */    private SyncFactory() {    }     /**     * The standard property-id for a synchronization provider implementation     * name.     */    public static final String ROWSET_SYNC_PROVIDER =            "rowset.provider.classname";    /**     * The standard property-id for a synchronization provider implementation     * vendor name.     */    public static final String ROWSET_SYNC_VENDOR =            "rowset.provider.vendor";    /**     * The standard property-id for a synchronization provider implementation     * version tag.     */    public static final String ROWSET_SYNC_PROVIDER_VERSION =            "rowset.provider.version";    /**     * The standard resource file name.     */    private static String ROWSET_PROPERTIES = "rowset.properties";    /**     * The RI Optimistic Provider.     */    private static String default_provider =            "com.sun.rowset.providers.RIOptimisticProvider";    /**     *  Permission required to invoke setJNDIContext and setLogger     */    private static final SQLPermission SET_SYNCFACTORY_PERMISSION =            new SQLPermission("setSyncFactory");    /**     * The initial JNDI context where <code>SyncProvider</code> implementations can     * be stored and from which they can be invoked.     */    private static Context ic;    /**     * The <code>Logger</code> object to be used by the <code>SyncFactory</code>.     */    private static volatile Logger rsLogger;    /**     *     */    private static Level rsLevel;    /**     * The registry of available <code>SyncProvider</code> implementations.     * See section 2.0 of the class comment for <code>SyncFactory</code> for an     * explanation of how a provider can be added to this registry.     */    private static Hashtable implementations;    /**     * Internal sync object used to maintain the SPI as a singleton     */    private static Object logSync = new Object();    /**     * Internal PrintWriter field for logging facility     */    private static java.io.PrintWriter logWriter = null;     /**     * Adds the the given synchronization provider to the factory register. Guidelines     * are provided in the <code>SyncProvider</code> specification for the     * required naming conventions for <code>SyncProvider</code>     * implementations.     * <p>     * Synchronization providers bound to a JNDI context can be     * registered by binding a SyncProvider instance to a JNDI namespace.     * <ul>     * <pre>     * SyncProvider p = new MySyncProvider();     * InitialContext ic = new InitialContext();     * ic.bind ("jdbc/rowset/MySyncProvider", p);     * </pre>     * </ul>     * Furthermore, an initial JNDI context should be set with the     * <code>SyncFactory</code> using the <code>setJNDIContext</code> method.     * The <code>SyncFactory</code> leverages this context to search for     * available <code>SyncProvider</code> objects bound to the JNDI     * context and its child nodes.     *     * @param providerID A <code>String</code> 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     */    public static synchronized void registerProvider(String providerID)            throws SyncFactoryException {         ProviderImpl impl = new ProviderImpl();        impl.setClassname(providerID);        initMapIfNecessary();        implementations.put(providerID, impl);     }     /**     * Returns the <code>SyncFactory</code> singleton.     *     * @return the <code>SyncFactory</code> instance     */    public static SyncFactory getSyncFactory() {        /*         * Using Initialization on Demand Holder idiom as         * Effective Java 2nd Edition,ITEM 71, indicates it is more performant         * than the Double-Check Locking idiom.         */        return SyncFactoryHolder.factory;    }     /**     * 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.     */    public static synchronized void unregisterProvider(String providerID)            throws SyncFactoryException {        initMapIfNecessary();        if (implementations.containsKey(providerID)) {            implementations.remove(providerID);        }    }    private static String colon = ":";    private static String strFileSep = "/";     private static synchronized void initMapIfNecessary() throws SyncFactoryException {         // 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 using Oracle'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;                    try (FileInputStream fis = new FileInputStream(ROWSET_PROPERTIES)) {                        properties.load(fis);                    }                    parseProperties(properties);                }                 /*                 * Always available                 */                ROWSET_PROPERTIES = "javax" + strFileSep + "sql" +                        strFileSep + "rowset" + strFileSep +                        "rowset.properties";                 ClassLoader cl = Thread.currentThread().getContextClassLoader();                 try (InputStream stream =                         (cl == null) ? ClassLoader.getSystemResourceAsStream(ROWSET_PROPERTIES)                                      : cl.getResourceAsStream(ROWSET_PROPERTIES)) {                    if (stream == null) {                        throw new SyncFactoryException(                            "Resource " + ROWSET_PROPERTIES + " not found");                    }                    properties.load(stream);                }                 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);            }        }    }     /**     * The internal debug switch.     */    private static boolean debug = false;    /**     * Internal registry count for the number of providers contained in the     * registry.     */    private static int providerImplIndex = 0;     /**     * Internal handler for all standard property parsing. Parses standard     * ROWSET properties and stores lazy references into the the internal registry.     */    private static void parseProperties(Properties p) {         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);            }        }    }     /**     * Used by the parseProperties methods to disassemble each property tuple.     */    private static String[] getPropertyNames(boolean append) {        return getPropertyNames(append, null);    }     /**     * Disassembles each property and its associated value. Also handles     * overloaded property names that contain indexes.     */    private static String[] getPropertyNames(boolean append,            String propertyIndex) {        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;

Lines 1–500 of 934.