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 return (SyncProvider) c.newInstance(); 586 587 } catch (IllegalAccessException e) { 588 throw new SyncFactoryException("IllegalAccessException: " + e.getMessage()); 589 } catch (InstantiationException e) { 590 throw new SyncFactoryException("InstantiationException: " + e.getMessage()); 591 } catch (ClassNotFoundException e) { 592 throw new SyncFactoryException("ClassNotFoundException: " + e.getMessage()); 593 } 594 } 595 596 /** 597 * Returns an Enumeration of currently registered synchronization 598 * providers. A <code>RowSet</code> implementation may use any provider in 599 * the enumeration as its <code>SyncProvider</code> object. 600 * <p> 601 * At a minimum, the reference synchronization provider allowing 602 * RowSet content data to be stored using a JDBC driver should be 603 * possible. 604 * 605 * @return Enumeration A enumeration of available synchronization 606 * providers that are registered with this Factory 607 * @throws SyncFactoryException If an error occurs obtaining the registered 608 * providers 609 */ 610 public static Enumeration<SyncProvider> getRegisteredProviders() 611 throws SyncFactoryException { 612 initMapIfNecessary(); 613 // return a collection of classnames 614 // of type SyncProvider 615 return implementations.elements(); 616 } 617 618 /** 619 * Sets the logging object to be used by the <code>SyncProvider</code> 620 * implementation provided by the <code>SyncFactory</code>. All 621 * <code>SyncProvider</code> implementations can log their events to 622 * this object and the application can retrieve a handle to this 623 * object using the <code>getLogger</code> method. 624 * <p> 625 * This method checks to see that there is an {@code SQLPermission} 626 * object which grants the permission {@code setSyncFactory} 627 * before allowing the method to succeed. If a 628 * {@code SecurityManager} exists and its 629 * {@code checkPermission} method denies calling {@code setLogger}, 630 * this method throws a 631 * {@code java.lang.SecurityException}. 632 * 633 * @param logger A Logger object instance 634 * @throws java.lang.SecurityException if a security manager exists and its 635 * {@code checkPermission} method denies calling {@code setLogger} 636 * @throws NullPointerException if the logger is null 637 * @see SecurityManager#checkPermission 638 */ 639 public static void setLogger(Logger logger) { 640 641 SecurityManager sec = System.getSecurityManager(); 642 if (sec != null) { 643 sec.checkPermission(SET_SYNCFACTORY_PERMISSION); 644 } 645 646 if(logger == null){ 647 throw new NullPointerException("You must provide a Logger"); 648 } 649 rsLogger = logger; 650 } 651 652 /** 653 * Sets the logging object that is used by <code>SyncProvider</code> 654 * implementations provided by the <code>SyncFactory</code> SPI. All 655 * <code>SyncProvider</code> implementations can log their events 656 * to this object and the application can retrieve a handle to this 657 * object using the <code>getLogger</code> method. 658 * <p> 659 * This method checks to see that there is an {@code SQLPermission} 660 * object which grants the permission {@code setSyncFactory} 661 * before allowing the method to succeed. If a 662 * {@code SecurityManager} exists and its 663 * {@code checkPermission} method denies calling {@code setLogger}, 664 * this method throws a 665 * {@code java.lang.SecurityException}. 666 * 667 * @param logger a Logger object instance 668 * @param level a Level object instance indicating the degree of logging 669 * required 670 * @throws java.lang.SecurityException if a security manager exists and its 671 * {@code checkPermission} method denies calling {@code setLogger} 672 * @throws NullPointerException if the logger is null 673 * @see SecurityManager#checkPermission 674 * @see LoggingPermission 675 */ 676 public static void setLogger(Logger logger, Level level) { 677 // singleton 678 SecurityManager sec = System.getSecurityManager(); 679 if (sec != null) { 680 sec.checkPermission(SET_SYNCFACTORY_PERMISSION); 681 } 682 683 if(logger == null){ 684 throw new NullPointerException("You must provide a Logger"); 685 } 686 logger.setLevel(level); 687 rsLogger = logger; 688 } 689 690 /** 691 * Returns the logging object for applications to retrieve 692 * synchronization events posted by SyncProvider implementations. 693 * @return The {@code Logger} that has been specified for use by 694 * {@code SyncProvider} implementations 695 * @throws SyncFactoryException if no logging object has been set. 696 */ 697 public static Logger getLogger() throws SyncFactoryException { 698 699 Logger result = rsLogger; 700 // only one logger per session 701 if (result == null) { 702 throw new SyncFactoryException("(SyncFactory) : No logger has been set"); 703 } 704 705 return result; 706 } 707 708 /** 709 * Sets the initial JNDI context from which SyncProvider implementations 710 * can be retrieved from a JNDI namespace 711 * <p> 712 * This method checks to see that there is an {@code SQLPermission} 713 * object which grants the permission {@code setSyncFactory} 714 * before allowing the method to succeed. If a 715 * {@code SecurityManager} exists and its 716 * {@code checkPermission} method denies calling {@code setJNDIContext}, 717 * this method throws a 718 * {@code java.lang.SecurityException}. 719 * 720 * @param ctx a valid JNDI context 721 * @throws SyncFactoryException if the supplied JNDI context is null 722 * @throws java.lang.SecurityException if a security manager exists and its 723 * {@code checkPermission} method denies calling {@code setJNDIContext} 724 * @see SecurityManager#checkPermission 725 */ 726 public static synchronized void setJNDIContext(javax.naming.Context ctx) 727 throws SyncFactoryException { 728 SecurityManager sec = System.getSecurityManager(); 729 if (sec != null) { 730 sec.checkPermission(SET_SYNCFACTORY_PERMISSION); 731 } 732 if (ctx == null) { 733 throw new SyncFactoryException("Invalid JNDI context supplied"); 734 } 735 ic = ctx; 736 } 737 738 /** 739 * Controls JNDI context initialization. 740 * 741 * @throws SyncFactoryException if an error occurs parsing the JNDI context 742 */ 743 private static synchronized void initJNDIContext() throws SyncFactoryException { 744 745 if ((ic != null) && (lazyJNDICtxRefresh == false)) { 746 try { 747 parseProperties(parseJNDIContext()); 748 lazyJNDICtxRefresh = true; // touch JNDI namespace once. 749 } catch (NamingException e) { 750 e.printStackTrace(); 751 throw new SyncFactoryException("SPI: NamingException: " + e.getExplanation()); 752 } catch (Exception e) { 753 e.printStackTrace(); 754 throw new SyncFactoryException("SPI: Exception: " + e.getMessage()); 755 } 756 } 757 } 758 /** 759 * Internal switch indicating whether the JNDI namespace should be re-read. 760 */ 761 private static boolean lazyJNDICtxRefresh = false; 762 763 /** 764 * Parses the set JNDI Context and passes bindings to the enumerateBindings 765 * method when complete. 766 */ 767 private static Properties parseJNDIContext() throws NamingException { 768 769 NamingEnumeration<?> bindings = ic.listBindings(""); 770 Properties properties = new Properties(); 771 772 // Hunt one level below context for available SyncProvider objects 773 enumerateBindings(bindings, properties); 774 775 return properties; 776 } 777 778 /** 779 * Scans each binding on JNDI context and determines if any binding is an 780 * instance of SyncProvider, if so, add this to the registry and continue to 781 * scan the current context using a re-entrant call to this method until all 782 * bindings have been enumerated. 783 */ 784 private static void enumerateBindings(NamingEnumeration<?> bindings, 785 Properties properties) throws NamingException { 786 787 boolean syncProviderObj = false; // move to parameters ? 788 789 try { 790 Binding bd = null; 791 Object elementObj = null; 792 String element = null; 793 while (bindings.hasMore()) { 794 bd = (Binding) bindings.next(); 795 element = bd.getName(); 796 elementObj = bd.getObject(); 797 798 if (!(ic.lookup(element) instanceof Context)) { 799 // skip directories/sub-contexts 800 if (ic.lookup(element) instanceof SyncProvider) { 801 syncProviderObj = true; 802 } 803 } 804 805 if (syncProviderObj) { 806 SyncProvider sync = (SyncProvider) elementObj; 807 properties.put(SyncFactory.ROWSET_SYNC_PROVIDER, 808 sync.getProviderID()); 809 syncProviderObj = false; // reset 810 } 811 812 } 813 } catch (javax.naming.NotContextException e) { 814 bindings.next(); 815 // Re-entrant call into method 816 enumerateBindings(bindings, properties); 817 } 818 } 819 820 /** 821 * Lazy initialization Holder class used by {@code getSyncFactory} 822 */ 823 private static class SyncFactoryHolder { 824 static final SyncFactory factory = new SyncFactory(); 825 } 826 } 827 828 /** 829 * Internal class that defines the lazy reference construct for each registered 830 * SyncProvider implementation. 831 */ 832 class ProviderImpl extends SyncProvider { 833 834 private String className = null; 835 private String vendorName = null; 836 private String ver = null; 837 private int index; 838 839 public void setClassname(String classname) { 840 className = classname; 841 } 842 843 public String getClassname() { 844 return className; 845 } 846 847 public void setVendor(String vendor) { 848 vendorName = vendor; 849 } 850 851 public String getVendor() { 852 return vendorName; 853 } 854 855 public void setVersion(String providerVer) { 856 ver = providerVer; 857 } 858 859 public String getVersion() { 860 return ver; 861 } 862 863 public void setIndex(int i) { 864 index = i; 865 } 866 867 public int getIndex() { 868 return index; 869 } 870 871 public int getDataSourceLock() throws SyncProviderException { 872 873 int dsLock = 0; 874 try { 875 dsLock = SyncFactory.getInstance(className).getDataSourceLock(); 876 } catch (SyncFactoryException sfEx) { 877 878 throw new SyncProviderException(sfEx.getMessage()); 879 } 880 881 return dsLock; 882 } 883 884 public int getProviderGrade() { 885 886 int grade = 0; 887 888 try { 889 grade = SyncFactory.getInstance(className).getProviderGrade(); 890 } catch (SyncFactoryException sfEx) { 891 // 892 } 893 894 return grade; 895 } 896 897 public String getProviderID() { 898 return className; 899 } 900 901 /* 902 public javax.sql.RowSetInternal getRowSetInternal() { 903 try 904 { 905 return SyncFactory.getInstance(className).getRowSetInternal(); 906 } catch(SyncFactoryException sfEx) { 907 // 908 } 909 } 910 */ 911 public javax.sql.RowSetReader getRowSetReader() { 912 913 RowSetReader rsReader = null; 914 915 try { 916 rsReader = SyncFactory.getInstance(className).getRowSetReader(); 917 } catch (SyncFactoryException sfEx) { 918 // 919 } 920 921 return rsReader; 922 923 } 924 925 public javax.sql.RowSetWriter getRowSetWriter() { 926 927 RowSetWriter rsWriter = null; 928 try { 929 rsWriter = SyncFactory.getInstance(className).getRowSetWriter(); 930 } catch (SyncFactoryException sfEx) { 931 // 932 } 933 934 return rsWriter; 935 } 936 937 public void setDataSourceLock(int param) 938 throws SyncProviderException { 939 940 try { 941 SyncFactory.getInstance(className).setDataSourceLock(param); 942 } catch (SyncFactoryException sfEx) { 943 944 throw new SyncProviderException(sfEx.getMessage()); 945 } 946 } 947 948 public int supportsUpdatableView() { 949 950 int view = 0; 951 952 try { 953 view = SyncFactory.getInstance(className).supportsUpdatableView(); 954 } catch (SyncFactoryException sfEx) { 955 // 956 } 957 958 return view; 959 } 960 }