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