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 | +} | |