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