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