1 /*
   2  * Copyright (c) 2003, 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 
  26 package javax.sql.rowset.spi;
  27 
  28 import java.util.logging.*;
  29 import java.util.*;
  30 
  31 import java.sql.*;
  32 import javax.sql.*;
  33 
  34 import java.io.FileInputStream;
  35 import java.io.InputStream;
  36 import java.io.IOException;
  37 import java.io.FileNotFoundException;
  38 import java.security.AccessController;
  39 import java.security.PrivilegedAction;
  40 import java.security.PrivilegedActionException;
  41 import java.security.PrivilegedExceptionAction;
  42 
  43 import javax.naming.*;
  44 import sun.reflect.misc.ReflectUtil;
  45 
  46 /**
  47  * The Service Provider Interface (SPI) mechanism that generates <code>SyncProvider</code>
  48  * instances to be used by disconnected <code>RowSet</code> objects.
  49  * The <code>SyncProvider</code> instances in turn provide the
  50  * <code>javax.sql.RowSetReader</code> object the <code>RowSet</code> object
  51  * needs to populate itself with data and the
  52  * <code>javax.sql.RowSetWriter</code> object it needs to
  53  * propagate changes to its
  54  * data back to the underlying data source.
  55  * <P>
  56  * Because the methods in the <code>SyncFactory</code> class are all static,
  57  * there is only one <code>SyncFactory</code> object
  58  * per Java VM at any one time. This ensures that there is a single source from which a
  59  * <code>RowSet</code> implementation can obtain its <code>SyncProvider</code>
  60  * implementation.
  61  *
  62  * <h3>1.0 Overview</h3>
  63  * The <code>SyncFactory</code> class provides an internal registry of available
  64  * synchronization provider implementations (<code>SyncProvider</code> objects).
  65  * This registry may be queried to determine which
  66  * synchronization providers are available.
  67  * The following line of code gets an enumeration of the providers currently registered.
  68  * <PRE>
  69  *     java.util.Enumeration e = SyncFactory.getRegisteredProviders();
  70  * </PRE>
  71  * All standard <code>RowSet</code> implementations must provide at least two providers:
  72  * <UL>
  73  *  <LI>an optimistic provider for use with a <code>CachedRowSet</code> implementation
  74  *     or an implementation derived from it
  75  *  <LI>an XML provider, which is used for reading and writing XML, such as with
  76  *       <code>WebRowSet</code> objects
  77  * </UL>
  78  * Note that the JDBC RowSet Implementations include the <code>SyncProvider</code>
  79  * implementations <code>RIOptimisticProvider</code> and <code>RIXmlProvider</code>,
  80  * which satisfy this requirement.
  81  * <P>
  82  * The <code>SyncFactory</code> class provides accessor methods to assist
  83  * applications in determining which synchronization providers are currently
  84  * registered with the <code>SyncFactory</code>.
  85  * <p>
  86  * Other methods let <code>RowSet</code> persistence providers be
  87  * registered or de-registered with the factory mechanism. This
  88  * allows additional synchronization provider implementations to be made
  89  * available to <code>RowSet</code> objects at run time.
  90  * <p>
  91  * Applications can apply a degree of filtering to determine the level of
  92  * synchronization that a <code>SyncProvider</code> implementation offers.
  93  * The following criteria determine whether a provider is
  94  * made available to a <code>RowSet</code> object:
  95  * <ol>
  96  * <li>If a particular provider is specified by a <code>RowSet</code> object, and
  97  * the <code>SyncFactory</code> does not contain a reference to this provider,
  98  * a <code>SyncFactoryException</code> is thrown stating that the synchronization
  99  * provider could not be found.
 100  *
 101  * <li>If a <code>RowSet</code> implementation is instantiated with a specified
 102  * provider and the specified provider has been properly registered, the
 103  * requested provider is supplied. Otherwise a <code>SyncFactoryException</code>
 104  * is thrown.
 105  *
 106  * <li>If a <code>RowSet</code> object does not specify a
 107  * <code>SyncProvider</code> implementation and no additional
 108  * <code>SyncProvider</code> implementations are available, the reference
 109  * implementation providers are supplied.
 110  * </ol>
 111  * <h3>2.0 Registering <code>SyncProvider</code> Implementations</h3>
 112  * <p>
 113  * Both vendors and developers can register <code>SyncProvider</code>
 114  * implementations using one of the following mechanisms.
 115  * <ul>
 116  * <LI><B>Using the command line</B><BR>
 117  * The name of the provider is supplied on the command line, which will add
 118  * the provider to the system properties.
 119  * For example:
 120  * <PRE>
 121  *    -Drowset.provider.classname=com.fred.providers.HighAvailabilityProvider
 122  * </PRE>
 123  * <li><b>Using the Standard Properties File</b><BR>
 124  * The reference implementation is targeted
 125  * to ship with J2SE 1.5, which will include an additional resource file
 126  * that may be edited by hand. Here is an example of the properties file
 127  * included in the reference implementation:
 128  * <PRE>
 129  *   #Default JDBC RowSet sync providers listing
 130  *   #
 131  *
 132  *   # Optimistic synchronization provider
 133  *   rowset.provider.classname.0=com.sun.rowset.providers.RIOptimisticProvider
 134  *   rowset.provider.vendor.0=Oracle Corporation
 135  *   rowset.provider.version.0=1.0
 136  *
 137  *   # XML Provider using standard XML schema
 138  *   rowset.provider.classname.1=com.sun.rowset.providers.RIXMLProvider
 139  *   rowset.provider.vendor.1=Oracle Corporation
 140  *   rowset.provider.version.1=1.0
 141  * </PRE>
 142  * The <code>SyncFactory</code> checks this file and registers the
 143  * <code>SyncProvider</code> implementations that it contains. A
 144  * developer or vendor can add other implementations to this file.
 145  * For example, here is a possible addition:
 146  * <PRE>
 147  *     rowset.provider.classname.2=com.fred.providers.HighAvailabilityProvider
 148  *     rowset.provider.vendor.2=Fred, Inc.
 149  *     rowset.provider.version.2=1.0
 150  * </PRE>
 151  *
 152  * <li><b>Using a JNDI Context</b><BR>
 153  * Available providers can be registered on a JNDI
 154  * context, and the <code>SyncFactory</code> will attempt to load
 155  * <code>SyncProvider</code> implementations from that JNDI context.
 156  * For example, the following code fragment registers a provider implementation
 157  * on a JNDI context.  This is something a deployer would normally do. In this
 158  * example, <code>MyProvider</code> is being registered on a CosNaming
 159  * namespace, which is the namespace used by J2EE resources.
 160  * <PRE>
 161  *    import javax.naming.*;
 162  *
 163  *    Hashtable svrEnv = new  Hashtable();
 164  *    srvEnv.put(Context.INITIAL_CONTEXT_FACTORY, "CosNaming");
 165  *
 166  *    Context ctx = new InitialContext(svrEnv);
 167  *    com.fred.providers.MyProvider = new MyProvider();
 168  *    ctx.rebind("providers/MyProvider", syncProvider);
 169  * </PRE>
 170  * </ul>
 171  * Next, an application will register the JNDI context with the
 172  * <code>SyncFactory</code> instance.  This allows the <code>SyncFactory</code>
 173  * to browse within the JNDI context looking for <code>SyncProvider</code>
 174  * implementations.
 175  * <PRE>
 176  *    Hashtable appEnv = new Hashtable();
 177  *    appEnv.put(Context.INITIAL_CONTEXT_FACTORY, "CosNaming");
 178  *    appEnv.put(Context.PROVIDER_URL, "iiop://hostname/providers");
 179  *    Context ctx = new InitialContext(appEnv);
 180  *
 181  *    SyncFactory.registerJNDIContext(ctx);
 182  * </PRE>
 183  * If a <code>RowSet</code> object attempts to obtain a <code>MyProvider</code>
 184  * object, the <code>SyncFactory</code> will try to locate it. First it searches
 185  * for it in the system properties, then it looks in the resource files, and
 186  * finally it checks the JNDI context that has been set. The <code>SyncFactory</code>
 187  * instance verifies that the requested provider is a valid extension of the
 188  * <code>SyncProvider</code> abstract class and then gives it to the
 189  * <code>RowSet</code> object. In the following code fragment, a new
 190  * <code>CachedRowSet</code> object is created and initialized with
 191  * <i>env</i>, which contains the binding to <code>MyProvider</code>.
 192  * <PRE>
 193  *    Hashtable env = new Hashtable();
 194  *    env.put(SyncFactory.ROWSET_SYNC_PROVIDER, "com.fred.providers.MyProvider");
 195  *    CachedRowSet crs = new com.sun.rowset.CachedRowSetImpl(env);
 196  * </PRE>
 197  * Further details on these mechanisms are available in the
 198  * <code>javax.sql.rowset.spi</code> package specification.
 199  *
 200  * @author  Jonathan Bruce
 201  * @see javax.sql.rowset.spi.SyncProvider
 202  * @see javax.sql.rowset.spi.SyncFactoryException
 203  * @since 1.5
 204  */
 205 public class SyncFactory {
 206 
 207     /**
 208      * Creates a new <code>SyncFactory</code> object, which is the singleton
 209      * instance.
 210      * Having a private constructor guarantees that no more than
 211      * one <code>SyncProvider</code> object can exist at a time.
 212      */
 213     private SyncFactory() {
 214     }
 215 
 216     /**
 217      * The standard property-id for a synchronization provider implementation
 218      * name.
 219      */
 220     public static final String ROWSET_SYNC_PROVIDER =
 221             "rowset.provider.classname";
 222     /**
 223      * The standard property-id for a synchronization provider implementation
 224      * vendor name.
 225      */
 226     public static final String ROWSET_SYNC_VENDOR =
 227             "rowset.provider.vendor";
 228     /**
 229      * The standard property-id for a synchronization provider implementation
 230      * version tag.
 231      */
 232     public static final String ROWSET_SYNC_PROVIDER_VERSION =
 233             "rowset.provider.version";
 234     /**
 235      * The standard resource file name.
 236      */
 237     private static String ROWSET_PROPERTIES = "rowset.properties";
 238 
 239     /**
 240      *  Permission required to invoke setJNDIContext and setLogger
 241      */
 242     private static final SQLPermission SET_SYNCFACTORY_PERMISSION =
 243             new SQLPermission("setSyncFactory");
 244     /**
 245      * The initial JNDI context where <code>SyncProvider</code> implementations can
 246      * be stored and from which they can be invoked.
 247      */
 248     private static Context ic;
 249     /**
 250      * The <code>Logger</code> object to be used by the <code>SyncFactory</code>.
 251      */
 252     private static volatile Logger rsLogger;
 253 
 254     /**
 255      * The registry of available <code>SyncProvider</code> implementations.
 256      * See section 2.0 of the class comment for <code>SyncFactory</code> for an
 257      * explanation of how a provider can be added to this registry.
 258      */
 259     private static Hashtable<String, SyncProvider> implementations;
 260 
 261     /**
 262      * Adds the the given synchronization provider to the factory register. Guidelines
 263      * are provided in the <code>SyncProvider</code> specification for the
 264      * required naming conventions for <code>SyncProvider</code>
 265      * implementations.
 266      * <p>
 267      * Synchronization providers bound to a JNDI context can be
 268      * registered by binding a SyncProvider instance to a JNDI namespace.
 269      *
 270      * <pre>
 271      * {@code
 272      * SyncProvider p = new MySyncProvider();
 273      * InitialContext ic = new InitialContext();
 274      * ic.bind ("jdbc/rowset/MySyncProvider", p);
 275      * } </pre>
 276      *
 277      * Furthermore, an initial JNDI context should be set with the
 278      * <code>SyncFactory</code> using the <code>setJNDIContext</code> method.
 279      * The <code>SyncFactory</code> leverages this context to search for
 280      * available <code>SyncProvider</code> objects bound to the JNDI
 281      * context and its child nodes.
 282      *
 283      * @param providerID A <code>String</code> object with the unique ID of the
 284      *             synchronization provider being registered
 285      * @throws SyncFactoryException if an attempt is made to supply an empty
 286      *         or null provider name
 287      * @see #setJNDIContext
 288      */
 289     public static synchronized void registerProvider(String providerID)
 290             throws SyncFactoryException {
 291 
 292         ProviderImpl impl = new ProviderImpl();
 293         impl.setClassname(providerID);
 294         initMapIfNecessary();
 295         implementations.put(providerID, impl);
 296 
 297     }
 298 
 299     /**
 300      * Returns the <code>SyncFactory</code> singleton.
 301      *
 302      * @return the <code>SyncFactory</code> instance
 303      */
 304     public static SyncFactory getSyncFactory() {
 305         /*
 306          * Using Initialization on Demand Holder idiom as
 307          * Effective Java 2nd Edition,ITEM 71, indicates it is more performant
 308          * than the Double-Check Locking idiom.
 309          */
 310         return SyncFactoryHolder.factory;
 311     }
 312 
 313     /**
 314      * Removes the designated currently registered synchronization provider from the
 315      * Factory SPI register.
 316      *
 317      * @param providerID The unique-id of the synchronization provider
 318      * @throws SyncFactoryException If an attempt is made to
 319      * unregister a SyncProvider implementation that was not registered.
 320      */
 321     public static synchronized void unregisterProvider(String providerID)
 322             throws SyncFactoryException {
 323         initMapIfNecessary();
 324         if (implementations.containsKey(providerID)) {
 325             implementations.remove(providerID);
 326         }
 327     }
 328     private static String colon = ":";
 329     private static String strFileSep = "/";
 330 
 331     private static synchronized void initMapIfNecessary() throws SyncFactoryException {
 332 
 333         // Local implementation class names and keys from Properties
 334         // file, translate names into Class objects using Class.forName
 335         // and store mappings
 336         final Properties properties = new Properties();
 337 
 338         if (implementations == null) {
 339             implementations = new Hashtable<>();
 340 
 341             try {
 342 
 343                 // check if user is supplying his Synchronisation Provider
 344                 // Implementation if not using Oracle's implementation.
 345                 // properties.load(new FileInputStream(ROWSET_PROPERTIES));
 346 
 347                 // The rowset.properties needs to be in jdk/jre/lib when
 348                 // integrated with jdk.
 349                 // else it should be picked from -D option from command line.
 350 
 351                 // -Drowset.properties will add to standard properties. Similar
 352                 // keys will over-write
 353 
 354                 /*
 355                  * Dependent on application
 356                  */
 357                 String strRowsetProperties;
 358                 try {
 359                     strRowsetProperties = AccessController.doPrivileged(new PrivilegedAction<String>() {
 360                         public String run() {
 361                             return System.getProperty("rowset.properties");
 362                         }
 363                     }, null, new PropertyPermission("rowset.properties", "read"));
 364                 } catch (Exception ex) {
 365                     System.out.println("errorget rowset.properties: " + ex);
 366                     strRowsetProperties = null;
 367                 };
 368 
 369                 if (strRowsetProperties != null) {
 370                     // Load user's implementation of SyncProvider
 371                     // here. -Drowset.properties=/abc/def/pqr.txt
 372                     ROWSET_PROPERTIES = strRowsetProperties;
 373                     try (FileInputStream fis = new FileInputStream(ROWSET_PROPERTIES)) {
 374                         properties.load(fis);
 375                     }
 376                     parseProperties(properties);
 377                 }
 378 
 379                 /*
 380                  * Always available
 381                  */
 382                 ROWSET_PROPERTIES = "javax" + strFileSep + "sql" +
 383                         strFileSep + "rowset" + strFileSep +
 384                         "rowset.properties";
 385 
 386                 ClassLoader cl = Thread.currentThread().getContextClassLoader();
 387 
 388                 try {
 389                     AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
 390                         try (InputStream stream = (cl == null) ?
 391                                 ClassLoader.getSystemResourceAsStream(ROWSET_PROPERTIES)
 392                                 : cl.getResourceAsStream(ROWSET_PROPERTIES)) {
 393                             if (stream == null) {
 394                                 throw new SyncFactoryException("Resource " + ROWSET_PROPERTIES + " not found");
 395                             }
 396                             properties.load(stream);
 397                         }
 398                         return null;
 399                     });
 400                 } catch (PrivilegedActionException ex) {
 401                     Throwable e = ex.getException();
 402                     if (e instanceof SyncFactoryException) {
 403                       throw (SyncFactoryException) e;
 404                     } else {
 405                         SyncFactoryException sfe = new SyncFactoryException();
 406                         sfe.initCause(ex.getException());
 407                         throw sfe;
 408                     }
 409                 }
 410 
 411                 parseProperties(properties);
 412 
 413             // removed else, has properties should sum together
 414 
 415             } catch (FileNotFoundException e) {
 416                 throw new SyncFactoryException("Cannot locate properties file: " + e);
 417             } catch (IOException e) {
 418                 throw new SyncFactoryException("IOException: " + e);
 419             }
 420 
 421             /*
 422              * Now deal with -Drowset.provider.classname
 423              * load additional properties from -D command line
 424              */
 425             properties.clear();
 426             String providerImpls;
 427             try {
 428                 providerImpls = AccessController.doPrivileged(new PrivilegedAction<String>() {
 429                     public String run() {
 430                         return System.getProperty(ROWSET_SYNC_PROVIDER);
 431                     }
 432                 }, null, new PropertyPermission(ROWSET_SYNC_PROVIDER, "read"));
 433             } catch (Exception ex) {
 434                 providerImpls = null;
 435             }
 436 
 437             if (providerImpls != null) {
 438                 int i = 0;
 439                 if (providerImpls.indexOf(colon) > 0) {
 440                     StringTokenizer tokenizer = new StringTokenizer(providerImpls, colon);
 441                     while (tokenizer.hasMoreElements()) {
 442                         properties.put(ROWSET_SYNC_PROVIDER + "." + i, tokenizer.nextToken());
 443                         i++;
 444                     }
 445                 } else {
 446                     properties.put(ROWSET_SYNC_PROVIDER, providerImpls);
 447                 }
 448                 parseProperties(properties);
 449             }
 450         }
 451     }
 452 
 453     /**
 454      * The internal debug switch.
 455      */
 456     private static boolean debug = false;
 457     /**
 458      * Internal registry count for the number of providers contained in the
 459      * registry.
 460      */
 461     private static int providerImplIndex = 0;
 462 
 463     /**
 464      * Internal handler for all standard property parsing. Parses standard
 465      * ROWSET properties and stores lazy references into the the internal registry.
 466      */
 467     private static void parseProperties(Properties p) {
 468 
 469         ProviderImpl impl = null;
 470         String key = null;
 471         String[] propertyNames = null;
 472 
 473         for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
 474 
 475             String str = (String) e.nextElement();
 476 
 477             int w = str.length();
 478 
 479             if (str.startsWith(SyncFactory.ROWSET_SYNC_PROVIDER)) {
 480 
 481                 impl = new ProviderImpl();
 482                 impl.setIndex(providerImplIndex++);
 483 
 484                 if (w == (SyncFactory.ROWSET_SYNC_PROVIDER).length()) {
 485                     // no property index has been set.
 486                     propertyNames = getPropertyNames(false);
 487                 } else {
 488                     // property index has been set.
 489                     propertyNames = getPropertyNames(true, str.substring(w - 1));
 490                 }
 491 
 492                 key = p.getProperty(propertyNames[0]);
 493                 impl.setClassname(key);
 494                 impl.setVendor(p.getProperty(propertyNames[1]));
 495                 impl.setVersion(p.getProperty(propertyNames[2]));
 496                 implementations.put(key, impl);
 497             }
 498         }
 499     }
 500 
 501     /**
 502      * Used by the parseProperties methods to disassemble each property tuple.
 503      */
 504     private static String[] getPropertyNames(boolean append) {
 505         return getPropertyNames(append, null);
 506     }
 507 
 508     /**
 509      * Disassembles each property and its associated value. Also handles
 510      * overloaded property names that contain indexes.
 511      */
 512     private static String[] getPropertyNames(boolean append,
 513             String propertyIndex) {
 514         String dot = ".";
 515         String[] propertyNames =
 516                 new String[]{SyncFactory.ROWSET_SYNC_PROVIDER,
 517             SyncFactory.ROWSET_SYNC_VENDOR,
 518             SyncFactory.ROWSET_SYNC_PROVIDER_VERSION};
 519         if (append) {
 520             for (int i = 0; i < propertyNames.length; i++) {
 521                 propertyNames[i] = propertyNames[i] +
 522                         dot +
 523                         propertyIndex;
 524             }
 525             return propertyNames;
 526         } else {
 527             return propertyNames;
 528         }
 529     }
 530 
 531     /**
 532      * Internal debug method that outputs the registry contents.
 533      */
 534     private static void showImpl(ProviderImpl impl) {
 535         System.out.println("Provider implementation:");
 536         System.out.println("Classname: " + impl.getClassname());
 537         System.out.println("Vendor: " + impl.getVendor());
 538         System.out.println("Version: " + impl.getVersion());
 539         System.out.println("Impl index: " + impl.getIndex());
 540     }
 541 
 542     /**
 543      * Returns the <code>SyncProvider</code> instance identified by <i>providerID</i>.
 544      *
 545      * @param providerID the unique identifier of the provider
 546      * @return a <code>SyncProvider</code> implementation
 547      * @throws SyncFactoryException If the SyncProvider cannot be found,
 548      * the providerID is {@code null}, or
 549      * some error was encountered when trying to invoke this provider.
 550      */
 551     public static SyncProvider getInstance(String providerID)
 552             throws SyncFactoryException {
 553 
 554         if(providerID == null) {
 555             throw new SyncFactoryException("The providerID cannot be null");
 556         }
 557 
 558         initMapIfNecessary(); // populate HashTable
 559         initJNDIContext();    // check JNDI context for any additional bindings
 560 
 561         ProviderImpl impl = (ProviderImpl) implementations.get(providerID);
 562 
 563         if (impl == null) {
 564             // Requested SyncProvider is unavailable. Return default provider.
 565             return new com.sun.rowset.providers.RIOptimisticProvider();
 566         }
 567 
 568         try {
 569             ReflectUtil.checkPackageAccess(providerID);
 570         } catch (java.security.AccessControlException e) {
 571             SyncFactoryException sfe = new SyncFactoryException();
 572             sfe.initCause(e);
 573             throw sfe;
 574         }
 575 
 576         // Attempt to invoke classname from registered SyncProvider list
 577         Class<?> c = null;
 578         try {
 579             ClassLoader cl = Thread.currentThread().getContextClassLoader();
 580 
 581             /**
 582              * The SyncProvider implementation of the user will be in
 583              * the classpath. We need to find the ClassLoader which loads
 584              * this SyncFactory and try to load the SyncProvider class from
 585              * there.
 586              **/
 587             c = Class.forName(providerID, true, cl);
 588 
 589             if (c != null) {
 590                 return (SyncProvider) c.newInstance();
 591             } else {
 592                 return new com.sun.rowset.providers.RIOptimisticProvider();
 593             }
 594 
 595         } catch (IllegalAccessException e) {
 596             throw new SyncFactoryException("IllegalAccessException: " + e.getMessage());
 597         } catch (InstantiationException e) {
 598             throw new SyncFactoryException("InstantiationException: " + e.getMessage());
 599         } catch (ClassNotFoundException e) {
 600             throw new SyncFactoryException("ClassNotFoundException: " + e.getMessage());
 601         }
 602     }
 603 
 604     /**
 605      * Returns an Enumeration of currently registered synchronization
 606      * providers.  A <code>RowSet</code> implementation may use any provider in
 607      * the enumeration as its <code>SyncProvider</code> object.
 608      * <p>
 609      * At a minimum, the reference synchronization provider allowing
 610      * RowSet content data to be stored using a JDBC driver should be
 611      * possible.
 612      *
 613      * @return Enumeration  A enumeration of available synchronization
 614      * providers that are registered with this Factory
 615      * @throws SyncFactoryException If an error occurs obtaining the registered
 616      * providers
 617      */
 618     public static Enumeration<SyncProvider> getRegisteredProviders()
 619             throws SyncFactoryException {
 620         initMapIfNecessary();
 621         // return a collection of classnames
 622         // of type SyncProvider
 623         return implementations.elements();
 624     }
 625 
 626     /**
 627      * Sets the logging object to be used by the <code>SyncProvider</code>
 628      * implementation provided by the <code>SyncFactory</code>. All
 629      * <code>SyncProvider</code> implementations can log their events to
 630      * this object and the application can retrieve a handle to this
 631      * object using the <code>getLogger</code> method.
 632      * <p>
 633      * This method checks to see that there is an {@code SQLPermission}
 634      * object  which grants the permission {@code setSyncFactory}
 635      * before allowing the method to succeed.  If a
 636      * {@code SecurityManager} exists and its
 637      * {@code checkPermission} method denies calling {@code setLogger},
 638      * this method throws a
 639      * {@code java.lang.SecurityException}.
 640      *
 641      * @param logger A Logger object instance
 642      * @throws java.lang.SecurityException if a security manager exists and its
 643      *   {@code checkPermission} method denies calling {@code setLogger}
 644      * @throws NullPointerException if the logger is null
 645      * @see SecurityManager#checkPermission
 646      */
 647     public static void setLogger(Logger logger) {
 648 
 649         SecurityManager sec = System.getSecurityManager();
 650         if (sec != null) {
 651             sec.checkPermission(SET_SYNCFACTORY_PERMISSION);
 652         }
 653 
 654         if(logger == null){
 655             throw new NullPointerException("You must provide a Logger");
 656         }
 657         rsLogger = logger;
 658     }
 659 
 660     /**
 661      * Sets the logging object that is used by <code>SyncProvider</code>
 662      * implementations provided by the <code>SyncFactory</code> SPI. All
 663      * <code>SyncProvider</code> implementations can log their events
 664      * to this object and the application can retrieve a handle to this
 665      * object using the <code>getLogger</code> method.
 666      * <p>
 667      * This method checks to see that there is an {@code SQLPermission}
 668      * object  which grants the permission {@code setSyncFactory}
 669      * before allowing the method to succeed.  If a
 670      * {@code SecurityManager} exists and its
 671      * {@code checkPermission} method denies calling {@code setLogger},
 672      * this method throws a
 673      * {@code java.lang.SecurityException}.
 674      *
 675      * @param logger a Logger object instance
 676      * @param level a Level object instance indicating the degree of logging
 677      * required
 678      * @throws java.lang.SecurityException if a security manager exists and its
 679      *   {@code checkPermission} method denies calling {@code setLogger}
 680      * @throws NullPointerException if the logger is null
 681      * @see SecurityManager#checkPermission
 682      * @see LoggingPermission
 683      */
 684     public static void setLogger(Logger logger, Level level) {
 685         // singleton
 686         SecurityManager sec = System.getSecurityManager();
 687         if (sec != null) {
 688             sec.checkPermission(SET_SYNCFACTORY_PERMISSION);
 689         }
 690 
 691         if(logger == null){
 692             throw new NullPointerException("You must provide a Logger");
 693         }
 694         logger.setLevel(level);
 695         rsLogger = logger;
 696     }
 697 
 698     /**
 699      * Returns the logging object for applications to retrieve
 700      * synchronization events posted by SyncProvider implementations.
 701      * @return The {@code Logger} that has been specified for use by
 702      * {@code SyncProvider} implementations
 703      * @throws SyncFactoryException if no logging object has been set.
 704      */
 705     public static Logger getLogger() throws SyncFactoryException {
 706 
 707         Logger result = rsLogger;
 708         // only one logger per session
 709         if (result == null) {
 710             throw new SyncFactoryException("(SyncFactory) : No logger has been set");
 711         }
 712 
 713         return result;
 714     }
 715 
 716     /**
 717      * Sets the initial JNDI context from which SyncProvider implementations
 718      * can be retrieved from a JNDI namespace
 719      * <p>
 720      *  This method checks to see that there is an {@code SQLPermission}
 721      * object  which grants the permission {@code setSyncFactory}
 722      * before allowing the method to succeed.  If a
 723      * {@code SecurityManager} exists and its
 724      * {@code checkPermission} method denies calling {@code setJNDIContext},
 725      * this method throws a
 726      * {@code java.lang.SecurityException}.
 727      *
 728      * @param ctx a valid JNDI context
 729      * @throws SyncFactoryException if the supplied JNDI context is null
 730      * @throws java.lang.SecurityException if a security manager exists and its
 731      *  {@code checkPermission} method denies calling {@code setJNDIContext}
 732      * @see SecurityManager#checkPermission
 733      */
 734     public static synchronized void setJNDIContext(javax.naming.Context ctx)
 735             throws SyncFactoryException {
 736         SecurityManager sec = System.getSecurityManager();
 737         if (sec != null) {
 738             sec.checkPermission(SET_SYNCFACTORY_PERMISSION);
 739         }
 740         if (ctx == null) {
 741             throw new SyncFactoryException("Invalid JNDI context supplied");
 742         }
 743         ic = ctx;
 744     }
 745 
 746     /**
 747      * Controls JNDI context initialization.
 748      *
 749      * @throws SyncFactoryException if an error occurs parsing the JNDI context
 750      */
 751     private static synchronized void initJNDIContext() throws SyncFactoryException {
 752 
 753         if ((ic != null) && (lazyJNDICtxRefresh == false)) {
 754             try {
 755                 parseProperties(parseJNDIContext());
 756                 lazyJNDICtxRefresh = true; // touch JNDI namespace once.
 757             } catch (NamingException e) {
 758                 e.printStackTrace();
 759                 throw new SyncFactoryException("SPI: NamingException: " + e.getExplanation());
 760             } catch (Exception e) {
 761                 e.printStackTrace();
 762                 throw new SyncFactoryException("SPI: Exception: " + e.getMessage());
 763             }
 764         }
 765     }
 766     /**
 767      * Internal switch indicating whether the JNDI namespace should be re-read.
 768      */
 769     private static boolean lazyJNDICtxRefresh = false;
 770 
 771     /**
 772      * Parses the set JNDI Context and passes bindings to the enumerateBindings
 773      * method when complete.
 774      */
 775     private static Properties parseJNDIContext() throws NamingException {
 776 
 777         NamingEnumeration<?> bindings = ic.listBindings("");
 778         Properties properties = new Properties();
 779 
 780         // Hunt one level below context for available SyncProvider objects
 781         enumerateBindings(bindings, properties);
 782 
 783         return properties;
 784     }
 785 
 786     /**
 787      * Scans each binding on JNDI context and determines if any binding is an
 788      * instance of SyncProvider, if so, add this to the registry and continue to
 789      * scan the current context using a re-entrant call to this method until all
 790      * bindings have been enumerated.
 791      */
 792     private static void enumerateBindings(NamingEnumeration<?> bindings,
 793             Properties properties) throws NamingException {
 794 
 795         boolean syncProviderObj = false; // move to parameters ?
 796 
 797         try {
 798             Binding bd = null;
 799             Object elementObj = null;
 800             String element = null;
 801             while (bindings.hasMore()) {
 802                 bd = (Binding) bindings.next();
 803                 element = bd.getName();
 804                 elementObj = bd.getObject();
 805 
 806                 if (!(ic.lookup(element) instanceof Context)) {
 807                     // skip directories/sub-contexts
 808                     if (ic.lookup(element) instanceof SyncProvider) {
 809                         syncProviderObj = true;
 810                     }
 811                 }
 812 
 813                 if (syncProviderObj) {
 814                     SyncProvider sync = (SyncProvider) elementObj;
 815                     properties.put(SyncFactory.ROWSET_SYNC_PROVIDER,
 816                             sync.getProviderID());
 817                     syncProviderObj = false; // reset
 818                 }
 819 
 820             }
 821         } catch (javax.naming.NotContextException e) {
 822             bindings.next();
 823             // Re-entrant call into method
 824             enumerateBindings(bindings, properties);
 825         }
 826     }
 827 
 828     /**
 829      * Lazy initialization Holder class used by {@code getSyncFactory}
 830      */
 831     private static class SyncFactoryHolder {
 832         static final SyncFactory factory = new SyncFactory();
 833     }
 834 }
 835 
 836 /**
 837  * Internal class that defines the lazy reference construct for each registered
 838  * SyncProvider implementation.
 839  */
 840 class ProviderImpl extends SyncProvider {
 841 
 842     private String className = null;
 843     private String vendorName = null;
 844     private String ver = null;
 845     private int index;
 846 
 847     public void setClassname(String classname) {
 848         className = classname;
 849     }
 850 
 851     public String getClassname() {
 852         return className;
 853     }
 854 
 855     public void setVendor(String vendor) {
 856         vendorName = vendor;
 857     }
 858 
 859     public String getVendor() {
 860         return vendorName;
 861     }
 862 
 863     public void setVersion(String providerVer) {
 864         ver = providerVer;
 865     }
 866 
 867     public String getVersion() {
 868         return ver;
 869     }
 870 
 871     public void setIndex(int i) {
 872         index = i;
 873     }
 874 
 875     public int getIndex() {
 876         return index;
 877     }
 878 
 879     public int getDataSourceLock() throws SyncProviderException {
 880 
 881         int dsLock = 0;
 882         try {
 883             dsLock = SyncFactory.getInstance(className).getDataSourceLock();
 884         } catch (SyncFactoryException sfEx) {
 885 
 886             throw new SyncProviderException(sfEx.getMessage());
 887         }
 888 
 889         return dsLock;
 890     }
 891 
 892     public int getProviderGrade() {
 893 
 894         int grade = 0;
 895 
 896         try {
 897             grade = SyncFactory.getInstance(className).getProviderGrade();
 898         } catch (SyncFactoryException sfEx) {
 899             //
 900         }
 901 
 902         return grade;
 903     }
 904 
 905     public String getProviderID() {
 906         return className;
 907     }
 908 
 909     /*
 910     public javax.sql.RowSetInternal getRowSetInternal() {
 911     try
 912     {
 913     return SyncFactory.getInstance(className).getRowSetInternal();
 914     } catch(SyncFactoryException sfEx) {
 915     //
 916     }
 917     }
 918      */
 919     public javax.sql.RowSetReader getRowSetReader() {
 920 
 921         RowSetReader rsReader = null;
 922 
 923         try {
 924             rsReader = SyncFactory.getInstance(className).getRowSetReader();
 925         } catch (SyncFactoryException sfEx) {
 926             //
 927         }
 928 
 929         return rsReader;
 930 
 931     }
 932 
 933     public javax.sql.RowSetWriter getRowSetWriter() {
 934 
 935         RowSetWriter rsWriter = null;
 936         try {
 937             rsWriter = SyncFactory.getInstance(className).getRowSetWriter();
 938         } catch (SyncFactoryException sfEx) {
 939             //
 940         }
 941 
 942         return rsWriter;
 943     }
 944 
 945     public void setDataSourceLock(int param)
 946             throws SyncProviderException {
 947 
 948         try {
 949             SyncFactory.getInstance(className).setDataSourceLock(param);
 950         } catch (SyncFactoryException sfEx) {
 951 
 952             throw new SyncProviderException(sfEx.getMessage());
 953         }
 954     }
 955 
 956     public int supportsUpdatableView() {
 957 
 958         int view = 0;
 959 
 960         try {
 961             view = SyncFactory.getInstance(className).supportsUpdatableView();
 962         } catch (SyncFactoryException sfEx) {
 963             //
 964         }
 965 
 966         return view;
 967     }
 968 }