Showing the changes to java/pkg/javax/sql/rowset/spi/SyncFactory.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,934 @@ | |||
| 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 | } | ||