1 /* 2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.sql.rowset.spi; 27 28 import java.util.logging.*; 29 import java.util.*; 30 31 import java.sql.*; 32 import javax.sql.*; 33 34 import java.io.FileInputStream; 35 import java.io.InputStream; 36 import java.io.IOException; 37 import java.io.FileNotFoundException; 38 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 /** 234 * Permission required to invoke setJNDIContext and setLogger 235 */ 236 private static final SQLPermission SET_SYNCFACTORY_PERMISSION = 237 new SQLPermission("setSyncFactory"); 238 /** 239 * The initial JNDI context where <code>SyncProvider</code> implementations can 240 * be stored and from which they can be invoked. 241 */ 242 private static Context ic; 243 /** 244 * The <code>Logger</code> object to be used by the <code>SyncFactory</code>. 245 */ 246 private static volatile Logger rsLogger; 247 248 /** 249 * The registry of available <code>SyncProvider</code> implementations. 250 * See section 2.0 of the class comment for <code>SyncFactory</code> for an 251 * explanation of how a provider can be added to this registry. 252 */ 253 private static Hashtable<String, SyncProvider> implementations; 254 255 /** 256 * Adds the the given synchronization provider to the factory register. Guidelines 257 * are provided in the <code>SyncProvider</code> specification for the 258 * required naming conventions for <code>SyncProvider</code> 259 * implementations. 260 * <p> 261 * Synchronization providers bound to a JNDI context can be 262 * registered by binding a SyncProvider instance to a JNDI namespace. 263 * 264 * <pre> 265 * {@code 266 * SyncProvider p = new MySyncProvider(); 267 * InitialContext ic = new InitialContext(); 268 * ic.bind ("jdbc/rowset/MySyncProvider", p); 269 * } </pre> 270 * 271 * Furthermore, an initial JNDI context should be set with the 272 * <code>SyncFactory</code> using the <code>setJNDIContext</code> method. 273 * The <code>SyncFactory</code> leverages this context to search for 274 * available <code>SyncProvider</code> objects bound to the JNDI 275 * context and its child nodes. 276 * 277 * @param providerID A <code>String</code> object with the unique ID of the 278 * synchronization provider being registered 279 * @throws SyncFactoryException if an attempt is made to supply an empty 280 * or null provider name 281 * @see #setJNDIContext 282 */ 283 public static synchronized void registerProvider(String providerID) 284 throws SyncFactoryException { 285 286 ProviderImpl impl = new ProviderImpl(); 287 impl.setClassname(providerID); 288 initMapIfNecessary(); 289 implementations.put(providerID, impl); 290 291 } 292 293 /** 294 * Returns the <code>SyncFactory</code> singleton. 295 * 296 * @return the <code>SyncFactory</code> instance 297 */ 298 public static SyncFactory getSyncFactory() { 299 /* 300 * Using Initialization on Demand Holder idiom as 301 * Effective Java 2nd Edition,ITEM 71, indicates it is more performant 302 * than the Double-Check Locking idiom. 303 */ 304 return SyncFactoryHolder.factory; 305 } 306 307 /** 308 * Removes the designated currently registered synchronization provider from the 309 * Factory SPI register. 310 * 311 * @param providerID The unique-id of the synchronization provider 312 * @throws SyncFactoryException If an attempt is made to 313 * unregister a SyncProvider implementation that was not registered. 314 */ 315 public static synchronized void unregisterProvider(String providerID) 316 throws SyncFactoryException { 317 initMapIfNecessary(); 318 if (implementations.containsKey(providerID)) { 319 implementations.remove(providerID); 320 } 321 } 322 private static String colon = ":"; 323 private static String strFileSep = "/"; 324 325 private static synchronized void initMapIfNecessary() throws SyncFactoryException { 326 327 // Local implementation class names and keys from Properties 328 // file, translate names into Class objects using Class.forName 329 // and store mappings 330 Properties properties = new Properties(); 331 332 if (implementations == null) { 333 implementations = new Hashtable<>(); 334 335 try { 336 337 // check if user is supplying his Synchronisation Provider 338 // Implementation if not using Oracle's implementation. 339 // properties.load(new FileInputStream(ROWSET_PROPERTIES)); 340 341 // The rowset.properties needs to be in jdk/jre/lib when 342 // integrated with jdk. 343 // else it should be picked from -D option from command line. 344 345 // -Drowset.properties will add to standard properties. Similar 346 // keys will over-write 347 348 /* 349 * Dependent on application 350 */ 351 String strRowsetProperties = System.getProperty("rowset.properties"); 352 if (strRowsetProperties != null) { 353 // Load user's implementation of SyncProvider 354 // here. -Drowset.properties=/abc/def/pqr.txt 355 ROWSET_PROPERTIES = strRowsetProperties; 356 try (FileInputStream fis = new FileInputStream(ROWSET_PROPERTIES)) { 357 properties.load(fis); 358 } 359 parseProperties(properties); 360 } 361 362 /* 363 * Always available 364 */ 365 ROWSET_PROPERTIES = "javax" + strFileSep + "sql" + 366 strFileSep + "rowset" + strFileSep + 367 "rowset.properties"; 368 369 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 370 371 try (InputStream stream = 372 (cl == null) ? ClassLoader.getSystemResourceAsStream(ROWSET_PROPERTIES) 373 : cl.getResourceAsStream(ROWSET_PROPERTIES)) { 374 if (stream == null) { 375 throw new SyncFactoryException( 376 "Resource " + ROWSET_PROPERTIES + " not found"); 377 } 378 properties.load(stream); 379 } 380 381 parseProperties(properties); 382 383 // removed else, has properties should sum together 384 385 } catch (FileNotFoundException e) { 386 throw new SyncFactoryException("Cannot locate properties file: " + e); 387 } catch (IOException e) { 388 throw new SyncFactoryException("IOException: " + e); 389 } 390 391 /* 392 * Now deal with -Drowset.provider.classname 393 * load additional properties from -D command line 394 */ 395 properties.clear(); 396 String providerImpls = System.getProperty(ROWSET_SYNC_PROVIDER); 397 398 if (providerImpls != null) { 399 int i = 0; 400 if (providerImpls.indexOf(colon) > 0) { 401 StringTokenizer tokenizer = new StringTokenizer(providerImpls, colon); 402 while (tokenizer.hasMoreElements()) { 403 properties.put(ROWSET_SYNC_PROVIDER + "." + i, tokenizer.nextToken()); 404 i++; 405 } 406 } else { 407 properties.put(ROWSET_SYNC_PROVIDER, providerImpls); 408 } 409 parseProperties(properties); 410 } 411 } 412 } 413 414 /** 415 * The internal debug switch. 416 */ 417 private static boolean debug = false; 418 /** 419 * Internal registry count for the number of providers contained in the 420 * registry. 421 */ 422 private static int providerImplIndex = 0; 423 424 /** 425 * Internal handler for all standard property parsing. Parses standard 426 * ROWSET properties and stores lazy references into the the internal registry. 427 */ 428 private static void parseProperties(Properties p) { 429 430 ProviderImpl impl = null; 431 String key = null; 432 String[] propertyNames = null; 433 434 for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) { 435 436 String str = (String) e.nextElement(); 437 438 int w = str.length(); 439 440 if (str.startsWith(SyncFactory.ROWSET_SYNC_PROVIDER)) { 441 442 impl = new ProviderImpl(); 443 impl.setIndex(providerImplIndex++); 444 445 if (w == (SyncFactory.ROWSET_SYNC_PROVIDER).length()) { 446 // no property index has been set. 447 propertyNames = getPropertyNames(false); 448 } else { 449 // property index has been set. 450 propertyNames = getPropertyNames(true, str.substring(w - 1)); 451 } 452 453 key = p.getProperty(propertyNames[0]); 454 impl.setClassname(key); 455 impl.setVendor(p.getProperty(propertyNames[1])); 456 impl.setVersion(p.getProperty(propertyNames[2])); 457 implementations.put(key, impl); 458 } 459 } 460 } 461 462 /** 463 * Used by the parseProperties methods to disassemble each property tuple. 464 */ 465 private static String[] getPropertyNames(boolean append) { 466 return getPropertyNames(append, null); 467 } 468 469 /** 470 * Disassembles each property and its associated value. Also handles 471 * overloaded property names that contain indexes. 472 */ 473 private static String[] getPropertyNames(boolean append, 474 String propertyIndex) { 475 String dot = "."; 476 String[] propertyNames = 477 new String[]{SyncFactory.ROWSET_SYNC_PROVIDER, 478 SyncFactory.ROWSET_SYNC_VENDOR, 479 SyncFactory.ROWSET_SYNC_PROVIDER_VERSION}; 480 if (append) { 481 for (int i = 0; i < propertyNames.length; i++) { 482 propertyNames[i] = propertyNames[i] + 483 dot + 484 propertyIndex; 485 } 486 return propertyNames; 487 } else { 488 return propertyNames; 489 } 490 } 491 492 /** 493 * Internal debug method that outputs the registry contents. 494 */ 495 private static void showImpl(ProviderImpl impl) { 496 System.out.println("Provider implementation:"); 497 System.out.println("Classname: " + impl.getClassname()); 498 System.out.println("Vendor: " + impl.getVendor()); 499 System.out.println("Version: " + impl.getVersion()); 500 System.out.println("Impl index: " + impl.getIndex()); 501 } 502 503 /** 504 * Returns the <code>SyncProvider</code> instance identified by <i>providerID</i>. 505 * 506 * @param providerID the unique identifier of the provider 507 * @return a <code>SyncProvider</code> implementation 508 * @throws SyncFactoryException If the SyncProvider cannot be found, 509 * the providerID is {@code null}, or 510 * some error was encountered when trying to invoke this provider. 511 */ 512 public static SyncProvider getInstance(String providerID) 513 throws SyncFactoryException { 514 515 if(providerID == null) { 516 throw new SyncFactoryException("The providerID cannot be null"); 517 } 518 519 initMapIfNecessary(); // populate HashTable 520 initJNDIContext(); // check JNDI context for any additional bindings 521 522 ProviderImpl impl = (ProviderImpl) implementations.get(providerID); 523 524 if (impl == null) { 525 // Requested SyncProvider is unavailable. Return default provider. 526 return new com.sun.rowset.providers.RIOptimisticProvider(); 527 } 528 529 // Attempt to invoke classname from registered SyncProvider list 530 Class<?> c = null; 531 try { 532 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 533 534 /** 535 * The SyncProvider implementation of the user will be in 536 * the classpath. We need to find the ClassLoader which loads 537 * this SyncFactory and try to laod the SyncProvider class from 538 * there. 539 **/ 540 c = Class.forName(providerID, true, cl); 541 542 if (c != null) { 543 return (SyncProvider) c.newInstance(); 544 } else { 545 return new com.sun.rowset.providers.RIOptimisticProvider(); 546 } 547 548 } catch (IllegalAccessException e) { 549 throw new SyncFactoryException("IllegalAccessException: " + e.getMessage()); 550 } catch (InstantiationException e) { 551 throw new SyncFactoryException("InstantiationException: " + e.getMessage()); 552 } catch (ClassNotFoundException e) { 553 throw new SyncFactoryException("ClassNotFoundException: " + e.getMessage()); 554 } 555 } 556 557 /** 558 * Returns an Enumeration of currently registered synchronization 559 * providers. A <code>RowSet</code> implementation may use any provider in 560 * the enumeration as its <code>SyncProvider</code> object. 561 * <p> 562 * At a minimum, the reference synchronization provider allowing 563 * RowSet content data to be stored using a JDBC driver should be 564 * possible. 565 * 566 * @return Enumeration A enumeration of available synchronization 567 * providers that are registered with this Factory 568 * @throws SyncFactoryException If an error occurs obtaining the registered 569 * providers 570 */ 571 public static Enumeration<SyncProvider> getRegisteredProviders() 572 throws SyncFactoryException { 573 initMapIfNecessary(); 574 // return a collection of classnames 575 // of type SyncProvider 576 return implementations.elements(); 577 } 578 579 /** 580 * Sets the logging object to be used by the <code>SyncProvider</code> 581 * implementation provided by the <code>SyncFactory</code>. All 582 * <code>SyncProvider</code> implementations can log their events to 583 * this object and the application can retrieve a handle to this 584 * object using the <code>getLogger</code> method. 585 * <p> 586 * This method checks to see that there is an {@code SQLPermission} 587 * object which grants the permission {@code setSyncFactory} 588 * before allowing the method to succeed. If a 589 * {@code SecurityManager} exists and its 590 * {@code checkPermission} method denies calling {@code setLogger}, 591 * this method throws a 592 * {@code java.lang.SecurityException}. 593 * 594 * @param logger A Logger object instance 595 * @throws java.lang.SecurityException if a security manager exists and its 596 * {@code checkPermission} method denies calling {@code setLogger} 597 * @throws NullPointerException if the logger is null 598 * @see SecurityManager#checkPermission 599 */ 600 public static void setLogger(Logger logger) { 601 602 SecurityManager sec = System.getSecurityManager(); 603 if (sec != null) { 604 sec.checkPermission(SET_SYNCFACTORY_PERMISSION); 605 } 606 607 if(logger == null){ 608 throw new NullPointerException("You must provide a Logger"); 609 } 610 rsLogger = logger; 611 } 612 613 /** 614 * Sets the logging object that is used by <code>SyncProvider</code> 615 * implementations provided by the <code>SyncFactory</code> SPI. All 616 * <code>SyncProvider</code> implementations can log their events 617 * to this object and the application can retrieve a handle to this 618 * object using the <code>getLogger</code> method. 619 * <p> 620 * This method checks to see that there is an {@code SQLPermission} 621 * object which grants the permission {@code setSyncFactory} 622 * before allowing the method to succeed. If a 623 * {@code SecurityManager} exists and its 624 * {@code checkPermission} method denies calling {@code setLogger}, 625 * this method throws a 626 * {@code java.lang.SecurityException}. 627 * 628 * @param logger a Logger object instance 629 * @param level a Level object instance indicating the degree of logging 630 * required 631 * @throws java.lang.SecurityException if a security manager exists and its 632 * {@code checkPermission} method denies calling {@code setLogger} 633 * @throws NullPointerException if the logger is null 634 * @see SecurityManager#checkPermission 635 * @see LoggingPermission 636 */ 637 public static void setLogger(Logger logger, Level level) { 638 // singleton 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 logger.setLevel(level); 648 rsLogger = logger; 649 } 650 651 /** 652 * Returns the logging object for applications to retrieve 653 * synchronization events posted by SyncProvider implementations. 654 * @return The {@code Logger} that has been specified for use by 655 * {@code SyncProvider} implementations 656 * @throws SyncFactoryException if no logging object has been set. 657 */ 658 public static Logger getLogger() throws SyncFactoryException { 659 660 Logger result = rsLogger; 661 // only one logger per session 662 if (result == null) { 663 throw new SyncFactoryException("(SyncFactory) : No logger has been set"); 664 } 665 666 return result; 667 } 668 669 /** 670 * Sets the initial JNDI context from which SyncProvider implementations 671 * can be retrieved from a JNDI namespace 672 * <p> 673 * This method checks to see that there is an {@code SQLPermission} 674 * object which grants the permission {@code setSyncFactory} 675 * before allowing the method to succeed. If a 676 * {@code SecurityManager} exists and its 677 * {@code checkPermission} method denies calling {@code setJNDIContext}, 678 * this method throws a 679 * {@code java.lang.SecurityException}. 680 * 681 * @param ctx a valid JNDI context 682 * @throws SyncFactoryException if the supplied JNDI context is null 683 * @throws java.lang.SecurityException if a security manager exists and its 684 * {@code checkPermission} method denies calling {@code setJNDIContext} 685 * @see SecurityManager#checkPermission 686 */ 687 public static synchronized void setJNDIContext(javax.naming.Context ctx) 688 throws SyncFactoryException { 689 SecurityManager sec = System.getSecurityManager(); 690 if (sec != null) { 691 sec.checkPermission(SET_SYNCFACTORY_PERMISSION); 692 } 693 if (ctx == null) { 694 throw new SyncFactoryException("Invalid JNDI context supplied"); 695 } 696 ic = ctx; 697 } 698 699 /** 700 * Controls JNDI context initialization. 701 * 702 * @throws SyncFactoryException if an error occurs parsing the JNDI context 703 */ 704 private static synchronized void initJNDIContext() throws SyncFactoryException { 705 706 if ((ic != null) && (lazyJNDICtxRefresh == false)) { 707 try { 708 parseProperties(parseJNDIContext()); 709 lazyJNDICtxRefresh = true; // touch JNDI namespace once. 710 } catch (NamingException e) { 711 e.printStackTrace(); 712 throw new SyncFactoryException("SPI: NamingException: " + e.getExplanation()); 713 } catch (Exception e) { 714 e.printStackTrace(); 715 throw new SyncFactoryException("SPI: Exception: " + e.getMessage()); 716 } 717 } 718 } 719 /** 720 * Internal switch indicating whether the JNDI namespace should be re-read. 721 */ 722 private static boolean lazyJNDICtxRefresh = false; 723 724 /** 725 * Parses the set JNDI Context and passes bindings to the enumerateBindings 726 * method when complete. 727 */ 728 private static Properties parseJNDIContext() throws NamingException { 729 730 NamingEnumeration<?> bindings = ic.listBindings(""); 731 Properties properties = new Properties(); 732 733 // Hunt one level below context for available SyncProvider objects 734 enumerateBindings(bindings, properties); 735 736 return properties; 737 } 738 739 /** 740 * Scans each binding on JNDI context and determines if any binding is an 741 * instance of SyncProvider, if so, add this to the registry and continue to 742 * scan the current context using a re-entrant call to this method until all 743 * bindings have been enumerated. 744 */ 745 private static void enumerateBindings(NamingEnumeration<?> bindings, 746 Properties properties) throws NamingException { 747 748 boolean syncProviderObj = false; // move to parameters ? 749 750 try { 751 Binding bd = null; 752 Object elementObj = null; 753 String element = null; 754 while (bindings.hasMore()) { 755 bd = (Binding) bindings.next(); 756 element = bd.getName(); 757 elementObj = bd.getObject(); 758 759 if (!(ic.lookup(element) instanceof Context)) { 760 // skip directories/sub-contexts 761 if (ic.lookup(element) instanceof SyncProvider) { 762 syncProviderObj = true; 763 } 764 } 765 766 if (syncProviderObj) { 767 SyncProvider sync = (SyncProvider) elementObj; 768 properties.put(SyncFactory.ROWSET_SYNC_PROVIDER, 769 sync.getProviderID()); 770 syncProviderObj = false; // reset 771 } 772 773 } 774 } catch (javax.naming.NotContextException e) { 775 bindings.next(); 776 // Re-entrant call into method 777 enumerateBindings(bindings, properties); 778 } 779 } 780 781 /** 782 * Lazy initialization Holder class used by {@code getSyncFactory} 783 */ 784 private static class SyncFactoryHolder { 785 static final SyncFactory factory = new SyncFactory(); 786 } 787 } 788 789 /** 790 * Internal class that defines the lazy reference construct for each registered 791 * SyncProvider implementation. 792 */ 793 class ProviderImpl extends SyncProvider { 794 795 private String className = null; 796 private String vendorName = null; 797 private String ver = null; 798 private int index; 799 800 public void setClassname(String classname) { 801 className = classname; 802 } 803 804 public String getClassname() { 805 return className; 806 } 807 808 public void setVendor(String vendor) { 809 vendorName = vendor; 810 } 811 812 public String getVendor() { 813 return vendorName; 814 } 815 816 public void setVersion(String providerVer) { 817 ver = providerVer; 818 } 819 820 public String getVersion() { 821 return ver; 822 } 823 824 public void setIndex(int i) { 825 index = i; 826 } 827 828 public int getIndex() { 829 return index; 830 } 831 832 public int getDataSourceLock() throws SyncProviderException { 833 834 int dsLock = 0; 835 try { 836 dsLock = SyncFactory.getInstance(className).getDataSourceLock(); 837 } catch (SyncFactoryException sfEx) { 838 839 throw new SyncProviderException(sfEx.getMessage()); 840 } 841 842 return dsLock; 843 } 844 845 public int getProviderGrade() { 846 847 int grade = 0; 848 849 try { 850 grade = SyncFactory.getInstance(className).getProviderGrade(); 851 } catch (SyncFactoryException sfEx) { 852 // 853 } 854 855 return grade; 856 } 857 858 public String getProviderID() { 859 return className; 860 } 861 862 /* 863 public javax.sql.RowSetInternal getRowSetInternal() { 864 try 865 { 866 return SyncFactory.getInstance(className).getRowSetInternal(); 867 } catch(SyncFactoryException sfEx) { 868 // 869 } 870 } 871 */ 872 public javax.sql.RowSetReader getRowSetReader() { 873 874 RowSetReader rsReader = null; 875 876 try { 877 rsReader = SyncFactory.getInstance(className).getRowSetReader(); 878 } catch (SyncFactoryException sfEx) { 879 // 880 } 881 882 return rsReader; 883 884 } 885 886 public javax.sql.RowSetWriter getRowSetWriter() { 887 888 RowSetWriter rsWriter = null; 889 try { 890 rsWriter = SyncFactory.getInstance(className).getRowSetWriter(); 891 } catch (SyncFactoryException sfEx) { 892 // 893 } 894 895 return rsWriter; 896 } 897 898 public void setDataSourceLock(int param) 899 throws SyncProviderException { 900 901 try { 902 SyncFactory.getInstance(className).setDataSourceLock(param); 903 } catch (SyncFactoryException sfEx) { 904 905 throw new SyncProviderException(sfEx.getMessage()); 906 } 907 } 908 909 public int supportsUpdatableView() { 910 911 int view = 0; 912 913 try { 914 view = SyncFactory.getInstance(className).supportsUpdatableView(); 915 } catch (SyncFactoryException sfEx) { 916 // 917 } 918 919 return view; 920 } 921 }