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