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