1 /*
2  * Copyright (c) 2003, 2019, 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  * <h2>1.0 Overview</h2>
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  * <h2>2.0 Registering <code>SyncProvider</code> Implementations</h2>
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      */
SyncFactory()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 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      */
registerProvider(String providerID)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      */
getSyncFactory()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      */
unregisterProvider(String providerID)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 
initMapIfNecessary()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                 try {
387                     AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
388                         InputStream in = SyncFactory.class.getModule().getResourceAsStream(ROWSET_PROPERTIES);
389                         if (in == null) {
390                             throw new SyncFactoryException("Resource " + ROWSET_PROPERTIES + " not found");
391                         }
392                         try (in) {
393                             properties.load(in);
394                         }
395                         return null;
396                     });
397                 } catch (PrivilegedActionException ex) {
398                     Throwable e = ex.getException();
399                     if (e instanceof SyncFactoryException) {
400                       throw (SyncFactoryException) e;
401                     } else {
402                         SyncFactoryException sfe = new SyncFactoryException();
403                         sfe.initCause(ex.getException());
404                         throw sfe;
405                     }
406                 }
407 
408                 parseProperties(properties);
409 
410             // removed else, has properties should sum together
411 
412             } catch (FileNotFoundException e) {
413                 throw new SyncFactoryException("Cannot locate properties file: " + e);
414             } catch (IOException e) {
415                 throw new SyncFactoryException("IOException: " + e);
416             }
417 
418             /*
419              * Now deal with -Drowset.provider.classname
420              * load additional properties from -D command line
421              */
422             properties.clear();
423             String providerImpls;
424             try {
425                 providerImpls = AccessController.doPrivileged(new PrivilegedAction<String>() {
426                     public String run() {
427                         return System.getProperty(ROWSET_SYNC_PROVIDER);
428                     }
429                 }, null, new PropertyPermission(ROWSET_SYNC_PROVIDER, "read"));
430             } catch (Exception ex) {
431                 providerImpls = null;
432             }
433 
434             if (providerImpls != null) {
435                 int i = 0;
436                 if (providerImpls.indexOf(colon) > 0) {
437                     StringTokenizer tokenizer = new StringTokenizer(providerImpls, colon);
438                     while (tokenizer.hasMoreElements()) {
439                         properties.put(ROWSET_SYNC_PROVIDER + "." + i, tokenizer.nextToken());
440                         i++;
441                     }
442                 } else {
443                     properties.put(ROWSET_SYNC_PROVIDER, providerImpls);
444                 }
445                 parseProperties(properties);
446             }
447         }
448     }
449 
450     /**
451      * The internal debug switch.
452      */
453     private static boolean debug = false;
454     /**
455      * Internal registry count for the number of providers contained in the
456      * registry.
457      */
458     private static int providerImplIndex = 0;
459 
460     /**
461      * Internal handler for all standard property parsing. Parses standard
462      * ROWSET properties and stores lazy references into the internal registry.
463      */
parseProperties(Properties p)464     private static void parseProperties(Properties p) {
465 
466         ProviderImpl impl = null;
467         String key = null;
468         String[] propertyNames = null;
469 
470         for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
471 
472             String str = (String) e.nextElement();
473 
474             int w = str.length();
475 
476             if (str.startsWith(SyncFactory.ROWSET_SYNC_PROVIDER)) {
477 
478                 impl = new ProviderImpl();
479                 impl.setIndex(providerImplIndex++);
480 
481                 if (w == (SyncFactory.ROWSET_SYNC_PROVIDER).length()) {
482                     // no property index has been set.
483                     propertyNames = getPropertyNames(false);
484                 } else {
485                     // property index has been set.
486                     propertyNames = getPropertyNames(true, str.substring(w - 1));
487                 }
488 
489                 key = p.getProperty(propertyNames[0]);
490                 impl.setClassname(key);
491                 impl.setVendor(p.getProperty(propertyNames[1]));
492                 impl.setVersion(p.getProperty(propertyNames[2]));
493                 implementations.put(key, impl);
494             }
495         }
496     }
497 
498     /**
499      * Used by the parseProperties methods to disassemble each property tuple.
500      */
getPropertyNames(boolean append)501     private static String[] getPropertyNames(boolean append) {
502         return getPropertyNames(append, null);
503     }
504 
505     /**
506      * Disassembles each property and its associated value. Also handles
507      * overloaded property names that contain indexes.
508      */
getPropertyNames(boolean append, String propertyIndex)509     private static String[] getPropertyNames(boolean append,
510             String propertyIndex) {
511         String dot = ".";
512         String[] propertyNames =
513                 new String[]{SyncFactory.ROWSET_SYNC_PROVIDER,
514             SyncFactory.ROWSET_SYNC_VENDOR,
515             SyncFactory.ROWSET_SYNC_PROVIDER_VERSION};
516         if (append) {
517             for (int i = 0; i < propertyNames.length; i++) {
518                 propertyNames[i] = propertyNames[i] +
519                         dot +
520                         propertyIndex;
521             }
522             return propertyNames;
523         } else {
524             return propertyNames;
525         }
526     }
527 
528     /**
529      * Internal debug method that outputs the registry contents.
530      */
showImpl(ProviderImpl impl)531     private static void showImpl(ProviderImpl impl) {
532         System.out.println("Provider implementation:");
533         System.out.println("Classname: " + impl.getClassname());
534         System.out.println("Vendor: " + impl.getVendor());
535         System.out.println("Version: " + impl.getVersion());
536         System.out.println("Impl index: " + impl.getIndex());
537     }
538 
539     /**
540      * Returns the <code>SyncProvider</code> instance identified by <i>providerID</i>.
541      *
542      * @param providerID the unique identifier of the provider
543      * @return a <code>SyncProvider</code> implementation
544      * @throws SyncFactoryException If the SyncProvider cannot be found,
545      * the providerID is {@code null}, or
546      * some error was encountered when trying to invoke this provider.
547      */
getInstance(String providerID)548     public static SyncProvider getInstance(String providerID)
549             throws SyncFactoryException {
550 
551         if(providerID == null) {
552             throw new SyncFactoryException("The providerID cannot be null");
553         }
554 
555         initMapIfNecessary(); // populate HashTable
556         initJNDIContext();    // check JNDI context for any additional bindings
557 
558         ProviderImpl impl = (ProviderImpl) implementations.get(providerID);
559 
560         if (impl == null) {
561             // Requested SyncProvider is unavailable. Return default provider.
562             return new com.sun.rowset.providers.RIOptimisticProvider();
563         }
564 
565         try {
566             ReflectUtil.checkPackageAccess(providerID);
567         } catch (java.security.AccessControlException e) {
568             SyncFactoryException sfe = new SyncFactoryException();
569             sfe.initCause(e);
570             throw sfe;
571         }
572 
573         // Attempt to invoke classname from registered SyncProvider list
574         Class<?> c = null;
575         try {
576             ClassLoader cl = Thread.currentThread().getContextClassLoader();
577 
578             /**
579              * The SyncProvider implementation of the user will be in
580              * the classpath. We need to find the ClassLoader which loads
581              * this SyncFactory and try to load the SyncProvider class from
582              * there.
583              **/
584             c = Class.forName(providerID, true, cl);
585             @SuppressWarnings("deprecation")
586             Object result =  c.newInstance();
587             return (SyncProvider)result;
588 
589         } catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) {
590             throw new SyncFactoryException("IllegalAccessException: " + e.getMessage());
591         }
592     }
593 
594     /**
595      * Returns an Enumeration of currently registered synchronization
596      * providers.  A <code>RowSet</code> implementation may use any provider in
597      * the enumeration as its <code>SyncProvider</code> object.
598      * <p>
599      * At a minimum, the reference synchronization provider allowing
600      * RowSet content data to be stored using a JDBC driver should be
601      * possible.
602      *
603      * @return Enumeration  A enumeration of available synchronization
604      * providers that are registered with this Factory
605      * @throws SyncFactoryException If an error occurs obtaining the registered
606      * providers
607      */
getRegisteredProviders()608     public static Enumeration<SyncProvider> getRegisteredProviders()
609             throws SyncFactoryException {
610         initMapIfNecessary();
611         // return a collection of classnames
612         // of type SyncProvider
613         return implementations.elements();
614     }
615 
616     /**
617      * Sets the logging object to be used by the <code>SyncProvider</code>
618      * implementation provided by the <code>SyncFactory</code>. All
619      * <code>SyncProvider</code> implementations can log their events to
620      * this object and the application can retrieve a handle to this
621      * object using the <code>getLogger</code> method.
622      * <p>
623      * This method checks to see that there is an {@code SQLPermission}
624      * object  which grants the permission {@code setSyncFactory}
625      * before allowing the method to succeed.  If a
626      * {@code SecurityManager} exists and its
627      * {@code checkPermission} method denies calling {@code setLogger},
628      * this method throws a
629      * {@code java.lang.SecurityException}.
630      *
631      * @param logger A Logger object instance
632      * @throws java.lang.SecurityException if a security manager exists and its
633      *   {@code checkPermission} method denies calling {@code setLogger}
634      * @throws NullPointerException if the logger is null
635      * @see SecurityManager#checkPermission
636      */
setLogger(Logger logger)637     public static void setLogger(Logger logger) {
638 
639         SecurityManager sec = System.getSecurityManager();
640         if (sec != null) {
641             sec.checkPermission(SET_SYNCFACTORY_PERMISSION);
642         }
643 
644         if(logger == null){
645             throw new NullPointerException("You must provide a Logger");
646         }
647         rsLogger = logger;
648     }
649 
650     /**
651      * Sets the logging object that is used by <code>SyncProvider</code>
652      * implementations provided by the <code>SyncFactory</code> SPI. All
653      * <code>SyncProvider</code> implementations can log their events
654      * to this object and the application can retrieve a handle to this
655      * object using the <code>getLogger</code> method.
656      * <p>
657      * This method checks to see that there is an {@code SQLPermission}
658      * object  which grants the permission {@code setSyncFactory}
659      * before allowing the method to succeed.  If a
660      * {@code SecurityManager} exists and its
661      * {@code checkPermission} method denies calling {@code setLogger},
662      * this method throws a
663      * {@code java.lang.SecurityException}.
664      *
665      * @param logger a Logger object instance
666      * @param level a Level object instance indicating the degree of logging
667      * required
668      * @throws java.lang.SecurityException if a security manager exists and its
669      *   {@code checkPermission} method denies calling {@code setLogger}
670      * @throws NullPointerException if the logger is null
671      * @see SecurityManager#checkPermission
672      * @see LoggingPermission
673      */
setLogger(Logger logger, Level level)674     public static void setLogger(Logger logger, Level level) {
675         // singleton
676         SecurityManager sec = System.getSecurityManager();
677         if (sec != null) {
678             sec.checkPermission(SET_SYNCFACTORY_PERMISSION);
679         }
680 
681         if(logger == null){
682             throw new NullPointerException("You must provide a Logger");
683         }
684         logger.setLevel(level);
685         rsLogger = logger;
686     }
687 
688     /**
689      * Returns the logging object for applications to retrieve
690      * synchronization events posted by SyncProvider implementations.
691      * @return The {@code Logger} that has been specified for use by
692      * {@code SyncProvider} implementations
693      * @throws SyncFactoryException if no logging object has been set.
694      */
getLogger()695     public static Logger getLogger() throws SyncFactoryException {
696 
697         Logger result = rsLogger;
698         // only one logger per session
699         if (result == null) {
700             throw new SyncFactoryException("(SyncFactory) : No logger has been set");
701         }
702 
703         return result;
704     }
705 
706     /**
707      * Sets the initial JNDI context from which SyncProvider implementations
708      * can be retrieved from a JNDI namespace
709      * <p>
710      *  This method checks to see that there is an {@code SQLPermission}
711      * object  which grants the permission {@code setSyncFactory}
712      * before allowing the method to succeed.  If a
713      * {@code SecurityManager} exists and its
714      * {@code checkPermission} method denies calling {@code setJNDIContext},
715      * this method throws a
716      * {@code java.lang.SecurityException}.
717      *
718      * @param ctx a valid JNDI context
719      * @throws SyncFactoryException if the supplied JNDI context is null
720      * @throws java.lang.SecurityException if a security manager exists and its
721      *  {@code checkPermission} method denies calling {@code setJNDIContext}
722      * @see SecurityManager#checkPermission
723      */
setJNDIContext(javax.naming.Context ctx)724     public static synchronized void setJNDIContext(javax.naming.Context ctx)
725             throws SyncFactoryException {
726         SecurityManager sec = System.getSecurityManager();
727         if (sec != null) {
728             sec.checkPermission(SET_SYNCFACTORY_PERMISSION);
729         }
730         if (ctx == null) {
731             throw new SyncFactoryException("Invalid JNDI context supplied");
732         }
733         ic = ctx;
734     }
735 
736     /**
737      * Controls JNDI context initialization.
738      *
739      * @throws SyncFactoryException if an error occurs parsing the JNDI context
740      */
initJNDIContext()741     private static synchronized void initJNDIContext() throws SyncFactoryException {
742 
743         if ((ic != null) && (lazyJNDICtxRefresh == false)) {
744             try {
745                 parseProperties(parseJNDIContext());
746                 lazyJNDICtxRefresh = true; // touch JNDI namespace once.
747             } catch (NamingException e) {
748                 e.printStackTrace();
749                 throw new SyncFactoryException("SPI: NamingException: " + e.getExplanation());
750             } catch (Exception e) {
751                 e.printStackTrace();
752                 throw new SyncFactoryException("SPI: Exception: " + e.getMessage());
753             }
754         }
755     }
756     /**
757      * Internal switch indicating whether the JNDI namespace should be re-read.
758      */
759     private static boolean lazyJNDICtxRefresh = false;
760 
761     /**
762      * Parses the set JNDI Context and passes bindings to the enumerateBindings
763      * method when complete.
764      */
parseJNDIContext()765     private static Properties parseJNDIContext() throws NamingException {
766 
767         NamingEnumeration<?> bindings = ic.listBindings("");
768         Properties properties = new Properties();
769 
770         // Hunt one level below context for available SyncProvider objects
771         enumerateBindings(bindings, properties);
772 
773         return properties;
774     }
775 
776     /**
777      * Scans each binding on JNDI context and determines if any binding is an
778      * instance of SyncProvider, if so, add this to the registry and continue to
779      * scan the current context using a re-entrant call to this method until all
780      * bindings have been enumerated.
781      */
enumerateBindings(NamingEnumeration<?> bindings, Properties properties)782     private static void enumerateBindings(NamingEnumeration<?> bindings,
783             Properties properties) throws NamingException {
784 
785         boolean syncProviderObj = false; // move to parameters ?
786 
787         try {
788             Binding bd = null;
789             Object elementObj = null;
790             String element = null;
791             while (bindings.hasMore()) {
792                 bd = (Binding) bindings.next();
793                 element = bd.getName();
794                 elementObj = bd.getObject();
795 
796                 if (!(ic.lookup(element) instanceof Context)) {
797                     // skip directories/sub-contexts
798                     if (ic.lookup(element) instanceof SyncProvider) {
799                         syncProviderObj = true;
800                     }
801                 }
802 
803                 if (syncProviderObj) {
804                     SyncProvider sync = (SyncProvider) elementObj;
805                     properties.put(SyncFactory.ROWSET_SYNC_PROVIDER,
806                             sync.getProviderID());
807                     syncProviderObj = false; // reset
808                 }
809 
810             }
811         } catch (javax.naming.NotContextException e) {
812             bindings.next();
813             // Re-entrant call into method
814             enumerateBindings(bindings, properties);
815         }
816     }
817 
818     /**
819      * Lazy initialization Holder class used by {@code getSyncFactory}
820      */
821     private static class SyncFactoryHolder {
822         static final SyncFactory factory = new SyncFactory();
823     }
824 }
825 
826 /**
827  * Internal class that defines the lazy reference construct for each registered
828  * SyncProvider implementation.
829  */
830 class ProviderImpl extends SyncProvider {
831 
832     private String className = null;
833     private String vendorName = null;
834     private String ver = null;
835     private int index;
836 
setClassname(String classname)837     public void setClassname(String classname) {
838         className = classname;
839     }
840 
getClassname()841     public String getClassname() {
842         return className;
843     }
844 
setVendor(String vendor)845     public void setVendor(String vendor) {
846         vendorName = vendor;
847     }
848 
getVendor()849     public String getVendor() {
850         return vendorName;
851     }
852 
setVersion(String providerVer)853     public void setVersion(String providerVer) {
854         ver = providerVer;
855     }
856 
getVersion()857     public String getVersion() {
858         return ver;
859     }
860 
setIndex(int i)861     public void setIndex(int i) {
862         index = i;
863     }
864 
getIndex()865     public int getIndex() {
866         return index;
867     }
868 
getDataSourceLock()869     public int getDataSourceLock() throws SyncProviderException {
870 
871         int dsLock = 0;
872         try {
873             dsLock = SyncFactory.getInstance(className).getDataSourceLock();
874         } catch (SyncFactoryException sfEx) {
875 
876             throw new SyncProviderException(sfEx.getMessage());
877         }
878 
879         return dsLock;
880     }
881 
getProviderGrade()882     public int getProviderGrade() {
883 
884         int grade = 0;
885 
886         try {
887             grade = SyncFactory.getInstance(className).getProviderGrade();
888         } catch (SyncFactoryException sfEx) {
889             //
890         }
891 
892         return grade;
893     }
894 
getProviderID()895     public String getProviderID() {
896         return className;
897     }
898 
899     /*
900     public javax.sql.RowSetInternal getRowSetInternal() {
901     try
902     {
903     return SyncFactory.getInstance(className).getRowSetInternal();
904     } catch(SyncFactoryException sfEx) {
905     //
906     }
907     }
908      */
getRowSetReader()909     public javax.sql.RowSetReader getRowSetReader() {
910 
911         RowSetReader rsReader = null;
912 
913         try {
914             rsReader = SyncFactory.getInstance(className).getRowSetReader();
915         } catch (SyncFactoryException sfEx) {
916             //
917         }
918 
919         return rsReader;
920 
921     }
922 
getRowSetWriter()923     public javax.sql.RowSetWriter getRowSetWriter() {
924 
925         RowSetWriter rsWriter = null;
926         try {
927             rsWriter = SyncFactory.getInstance(className).getRowSetWriter();
928         } catch (SyncFactoryException sfEx) {
929             //
930         }
931 
932         return rsWriter;
933     }
934 
setDataSourceLock(int param)935     public void setDataSourceLock(int param)
936             throws SyncProviderException {
937 
938         try {
939             SyncFactory.getInstance(className).setDataSourceLock(param);
940         } catch (SyncFactoryException sfEx) {
941 
942             throw new SyncProviderException(sfEx.getMessage());
943         }
944     }
945 
supportsUpdatableView()946     public int supportsUpdatableView() {
947 
948         int view = 0;
949 
950         try {
951             view = SyncFactory.getInstance(className).supportsUpdatableView();
952         } catch (SyncFactoryException sfEx) {
953             //
954         }
955 
956         return view;
957     }
958 }
959