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