View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.connector.meta.jdbc;
25  
26  import java.beans.PropertyVetoException;
27  import java.util.Hashtable;
28  import java.util.Map;
29  import javax.naming.Context;
30  import javax.naming.InitialContext;
31  import javax.naming.Reference;
32  import javax.naming.StringRefAddr;
33  import javax.naming.spi.ObjectFactory;
34  import javax.sql.DataSource;
35  import net.jcip.annotations.ThreadSafe;
36  import org.modeshape.common.annotation.Category;
37  import org.modeshape.common.annotation.Description;
38  import org.modeshape.common.annotation.Label;
39  import org.modeshape.common.i18n.I18n;
40  import org.modeshape.common.util.Logger;
41  import org.modeshape.connector.meta.jdbc.JdbcMetadataRepository.JdbcMetadataTransaction;
42  import org.modeshape.graph.ExecutionContext;
43  import org.modeshape.graph.connector.RepositoryConnection;
44  import org.modeshape.graph.connector.RepositorySourceCapabilities;
45  import org.modeshape.graph.connector.RepositorySourceException;
46  import org.modeshape.graph.connector.base.AbstractRepositorySource;
47  import org.modeshape.graph.connector.base.Connection;
48  import org.modeshape.graph.connector.base.PathNode;
49  import org.modeshape.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
50  import com.mchange.v2.c3p0.ComboPooledDataSource;
51  
52  @ThreadSafe
53  public class JdbcMetadataSource extends AbstractRepositorySource implements ObjectFactory {
54  
55      private static final long serialVersionUID = 1L;
56  
57      private static final Logger LOGGER = Logger.getLogger(JdbcMetadataSource.class);
58  
59      protected static final String SOURCE_NAME = "sourceName";
60      protected static final String ROOT_NODE_UUID = "rootNodeUuid";
61      protected static final String DATA_SOURCE_JNDI_NAME = "dataSourceJndiName";
62      protected static final String USERNAME = "username";
63      protected static final String PASSWORD = "password";
64      protected static final String URL = "url";
65      protected static final String DRIVER_CLASS_NAME = "driverClassName";
66      protected static final String DRIVER_CLASSLOADER_NAME = "driverClassloaderName";
67      protected static final String MAXIMUM_CONNECTIONS_IN_POOL = "maximumConnectionsInPool";
68      protected static final String MINIMUM_CONNECTIONS_IN_POOL = "minimumConnectionsInPool";
69      protected static final String MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS = "maximumConnectionIdleTimeInSeconds";
70      protected static final String MAXIMUM_SIZE_OF_STATEMENT_CACHE = "maximumSizeOfStatementCache";
71      protected static final String NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED = "numberOfConnectionsToBeAcquiredAsNeeded";
72      protected static final String IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS = "idleTimeInSecondsBeforeTestingConnections";
73      protected static final String CACHE_TIME_TO_LIVE_IN_MILLISECONDS = "cacheTimeToLiveInMilliseconds";
74      protected static final String RETRY_LIMIT = "retryLimit";
75      protected static final String DEFAULT_WORKSPACE = "defaultWorkspace";
76      protected static final String DEFAULT_CATALOG_NAME = "defaultCatalogName";
77      protected static final String DEFAULT_SCHEMA_NAME = "defaultSchemaName";
78      protected static final String METADATA_COLLECTOR_CLASS_NAME = "metadataCollectorClassName";
79  
80      /**
81       * This source does not support events.
82       */
83      protected static final boolean SUPPORTS_EVENTS = false;
84      /**
85       * This source does support same-name-siblings for procedure nodes.
86       */
87      protected static final boolean SUPPORTS_SAME_NAME_SIBLINGS = true;
88      /**
89       * This source does not support creating references.
90       */
91      protected static final boolean SUPPORTS_REFERENCES = false;
92      /**
93       * This source does not support updates.
94       */
95      public static final boolean SUPPORTS_UPDATES = false;
96      /**
97       * This source does not support creating workspaces.
98       */
99      public static final boolean SUPPORTS_CREATING_WORKSPACES = false;
100 
101     /**
102      * The initial {@link #getDefaultWorkspaceName() name of the default workspace} is "{@value} ", unless otherwise specified.
103      */
104     public static final String DEFAULT_NAME_OF_DEFAULT_WORKSPACE = "default";
105 
106     /**
107      * The initial {@link #getDefaultCatalogName() catalog name for databases that do not support catalogs} is "{@value} ", unless
108      * otherwise specified.
109      */
110     public static final String DEFAULT_NAME_OF_DEFAULT_CATALOG = "default";
111 
112     /**
113      * The initial {@link #getDefaultSchemaName() schema name for databases that do not support schemas} is "{@value} ", unless
114      * otherwise specified.
115      */
116     public static final String DEFAULT_NAME_OF_DEFAULT_SCHEMA = "default";
117 
118     private static final int DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL = 5;
119     private static final int DEFAULT_MINIMUM_CONNECTIONS_IN_POOL = 0;
120     private static final int DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS = 60 * 10; // 10 minutes
121     private static final int DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE = 100;
122     private static final int DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED = 1;
123     private static final int DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS = 60 * 3; // 3 minutes
124     private static final MetadataCollector DEFAULT_METADATA_COLLECTOR = new JdbcMetadataCollector();
125 
126     @Description( i18n = JdbcMetadataI18n.class, value = "dataSourceJndiNamePropertyDescription" )
127     @Label( i18n = JdbcMetadataI18n.class, value = "dataSourceJndiNamePropertyLabel" )
128     @Category( i18n = JdbcMetadataI18n.class, value = "dataSourceJndiNamePropertyCategory" )
129     private volatile String dataSourceJndiName;
130 
131     @Description( i18n = JdbcMetadataI18n.class, value = "usernamePropertyDescription" )
132     @Label( i18n = JdbcMetadataI18n.class, value = "usernamePropertyLabel" )
133     @Category( i18n = JdbcMetadataI18n.class, value = "usernamePropertyCategory" )
134     private volatile String username;
135 
136     @Description( i18n = JdbcMetadataI18n.class, value = "passwordPropertyDescription" )
137     @Label( i18n = JdbcMetadataI18n.class, value = "passwordPropertyLabel" )
138     @Category( i18n = JdbcMetadataI18n.class, value = "passwordPropertyCategory" )
139     private volatile String password;
140 
141     @Description( i18n = JdbcMetadataI18n.class, value = "urlPropertyDescription" )
142     @Label( i18n = JdbcMetadataI18n.class, value = "urlPropertyLabel" )
143     @Category( i18n = JdbcMetadataI18n.class, value = "urlPropertyCategory" )
144     private volatile String url;
145 
146     @Description( i18n = JdbcMetadataI18n.class, value = "driverClassNamePropertyDescription" )
147     @Label( i18n = JdbcMetadataI18n.class, value = "driverClassNamePropertyLabel" )
148     @Category( i18n = JdbcMetadataI18n.class, value = "driverClassNamePropertyCategory" )
149     private volatile String driverClassName;
150 
151     // @Description( i18n = JdbcMetadataI18n.class, value = "driverClassloaderNamePropertyDescription" )
152     // @Label( i18n = JdbcMetadataI18n.class, value = "driverClassloaderNamePropertyLabel" )
153     // @Category( i18n = JdbcMetadataI18n.class, value = "driverClassloaderNamePropertyCategory" )
154     private volatile String driverClassloaderName;
155 
156     @Description( i18n = JdbcMetadataI18n.class, value = "maximumConnectionsInPoolPropertyDescription" )
157     @Label( i18n = JdbcMetadataI18n.class, value = "maximumConnectionsInPoolPropertyLabel" )
158     @Category( i18n = JdbcMetadataI18n.class, value = "maximumConnectionsInPoolPropertyCategory" )
159     private volatile int maximumConnectionsInPool = DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL;
160 
161     @Description( i18n = JdbcMetadataI18n.class, value = "minimumConnectionsInPoolPropertyDescription" )
162     @Label( i18n = JdbcMetadataI18n.class, value = "minimumConnectionsInPoolPropertyLabel" )
163     @Category( i18n = JdbcMetadataI18n.class, value = "minimumConnectionsInPoolPropertyCategory" )
164     private volatile int minimumConnectionsInPool = DEFAULT_MINIMUM_CONNECTIONS_IN_POOL;
165 
166     @Description( i18n = JdbcMetadataI18n.class, value = "maximumConnectionIdleTimeInSecondsPropertyDescription" )
167     @Label( i18n = JdbcMetadataI18n.class, value = "maximumConnectionIdleTimeInSecondsPropertyLabel" )
168     @Category( i18n = JdbcMetadataI18n.class, value = "maximumConnectionIdleTimeInSecondsPropertyCategory" )
169     private volatile int maximumConnectionIdleTimeInSeconds = DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS;
170 
171     @Description( i18n = JdbcMetadataI18n.class, value = "maximumSizeOfStatementCachePropertyDescription" )
172     @Label( i18n = JdbcMetadataI18n.class, value = "maximumSizeOfStatementCachePropertyLabel" )
173     @Category( i18n = JdbcMetadataI18n.class, value = "maximumSizeOfStatementCachePropertyCategory" )
174     private volatile int maximumSizeOfStatementCache = DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE;
175 
176     @Description( i18n = JdbcMetadataI18n.class, value = "numberOfConnectionsToAcquireAsNeededPropertyDescription" )
177     @Label( i18n = JdbcMetadataI18n.class, value = "numberOfConnectionsToAcquireAsNeededPropertyLabel" )
178     @Category( i18n = JdbcMetadataI18n.class, value = "numberOfConnectionsToAcquireAsNeededPropertyCategory" )
179     private volatile int numberOfConnectionsToAcquireAsNeeded = DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED;
180 
181     @Description( i18n = JdbcMetadataI18n.class, value = "idleTimeInSecondsBeforeTestingConnectionsPropertyDescription" )
182     @Label( i18n = JdbcMetadataI18n.class, value = "idleTimeInSecondsBeforeTestingConnectionsPropertyLabel" )
183     @Category( i18n = JdbcMetadataI18n.class, value = "idleTimeInSecondsBeforeTestingConnectionsPropertyCategory" )
184     private volatile int idleTimeInSecondsBeforeTestingConnections = DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS;
185 
186     @Description( i18n = JdbcMetadataI18n.class, value = "defaultWorkspaceNamePropertyDescription" )
187     @Label( i18n = JdbcMetadataI18n.class, value = "defaultWorkspaceNamePropertyLabel" )
188     @Category( i18n = JdbcMetadataI18n.class, value = "defaultWorkspaceNamePropertyCategory" )
189     private volatile String defaultWorkspace = DEFAULT_NAME_OF_DEFAULT_WORKSPACE;
190 
191     @Description( i18n = JdbcMetadataI18n.class, value = "defaultCatalogNamePropertyDescription" )
192     @Label( i18n = JdbcMetadataI18n.class, value = "defaultCatalogNamePropertyLabel" )
193     @Category( i18n = JdbcMetadataI18n.class, value = "defaultCatalogNamePropertyCategory" )
194     private volatile String defaultCatalogName = DEFAULT_NAME_OF_DEFAULT_CATALOG;
195 
196     @Description( i18n = JdbcMetadataI18n.class, value = "defaultSchemaNamePropertyDescription" )
197     @Label( i18n = JdbcMetadataI18n.class, value = "defaultSchemaNamePropertyLabel" )
198     @Category( i18n = JdbcMetadataI18n.class, value = "defaultSchemaNamePropertyCategory" )
199     private volatile String defaultSchemaName = DEFAULT_NAME_OF_DEFAULT_SCHEMA;
200 
201     @Description( i18n = JdbcMetadataI18n.class, value = "metadataCollectorClassNamePropertyDescription" )
202     @Label( i18n = JdbcMetadataI18n.class, value = "metadataCollectorClassNamePropertyLabel" )
203     @Category( i18n = JdbcMetadataI18n.class, value = "metadataCollectorClassNamePropertyCategory" )
204     private volatile String metadataCollectorClassName = DEFAULT_METADATA_COLLECTOR.getClass().getName();
205 
206     private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(SUPPORTS_SAME_NAME_SIBLINGS,
207                                                                                                   SUPPORTS_UPDATES,
208                                                                                                   SUPPORTS_EVENTS,
209                                                                                                   SUPPORTS_CREATING_WORKSPACES,
210                                                                                                   SUPPORTS_REFERENCES);
211     private transient DataSource dataSource;
212     private transient JdbcMetadataRepository repository;
213     private transient MetadataCollector metadataCollector = DEFAULT_METADATA_COLLECTOR;
214 
215     private ExecutionContext defaultContext = new ExecutionContext();
216 
217     final JdbcMetadataRepository repository() {
218         return this.repository;
219     }
220 
221     @Override
222     public void close() {
223         if (this.dataSource instanceof ComboPooledDataSource) {
224             ((ComboPooledDataSource)this.dataSource).close();
225         }
226     }
227 
228     /**
229      * @return the datasource that corresponds to the login information provided to this source
230      * @see #setUsername(String)
231      * @see #setPassword(String)
232      * @see #setDriverClassName(String)
233      * @see #setDriverClassloaderName(String)
234      * @see #setUrl(String)
235      * @see #setDataSourceJndiName(String)
236      */
237     DataSource getDataSource() {
238         if (this.dataSource == null) {
239             loadDataSource();
240         }
241         return this.dataSource;
242     }
243 
244     public RepositorySourceCapabilities getCapabilities() {
245         return this.capabilities;
246     }
247 
248     /*
249      * Synchronized to avoid race conditions with setters of datasource-related properties
250      */
251     private synchronized void loadDataSource() throws RepositorySourceException {
252         // Now set the mandatory information, overwriting anything that the subclasses may have tried ...
253         if (this.dataSource == null && this.dataSourceJndiName != null) {
254             // Try to load the DataSource from JNDI ...
255             try {
256                 Context context = new InitialContext();
257                 dataSource = (DataSource)context.lookup(this.dataSourceJndiName);
258             } catch (Throwable t) {
259                 LOGGER.error(t, JdbcMetadataI18n.errorFindingDataSourceInJndi, getName(), dataSourceJndiName);
260             }
261         }
262 
263         if (this.dataSource == null) {
264             // Set the context class loader, so that the driver could be found ...
265             if (this.repositoryContext != null && this.driverClassloaderName != null) {
266                 try {
267                     ExecutionContext context = this.repositoryContext.getExecutionContext();
268                     ClassLoader loader = context.getClassLoader(this.driverClassloaderName);
269                     if (loader != null) {
270                         Thread.currentThread().setContextClassLoader(loader);
271                     }
272                 } catch (Throwable t) {
273                     I18n msg = JdbcMetadataI18n.errorSettingContextClassLoader;
274                     LOGGER.error(t, msg, getName(), driverClassloaderName);
275                 }
276             }
277 
278             if (this.driverClassName == null || this.url == null) {
279                 throw new RepositorySourceException(JdbcMetadataI18n.driverClassNameAndUrlAreRequired.text(driverClassName, url));
280             }
281 
282             ComboPooledDataSource cpds = new ComboPooledDataSource();
283 
284             try {
285                 cpds.setDriverClass(this.driverClassName);
286                 cpds.setJdbcUrl(this.url);
287                 cpds.setUser(this.username);
288                 cpds.setPassword(this.password);
289                 cpds.setMaxStatements(this.maximumSizeOfStatementCache);
290                 cpds.setAcquireRetryAttempts(retryLimit);
291                 cpds.setMaxIdleTime(this.maximumConnectionIdleTimeInSeconds);
292                 cpds.setMinPoolSize(this.minimumConnectionsInPool);
293                 cpds.setMaxPoolSize(this.maximumConnectionsInPool);
294                 cpds.setAcquireIncrement(this.numberOfConnectionsToAcquireAsNeeded);
295                 cpds.setIdleConnectionTestPeriod(this.idleTimeInSecondsBeforeTestingConnections);
296 
297             } catch (PropertyVetoException pve) {
298                 throw new IllegalStateException(JdbcMetadataI18n.couldNotSetDriverProperties.text(), pve);
299             }
300 
301             this.dataSource = cpds;
302         }
303 
304     }
305 
306     public RepositoryConnection getConnection() throws RepositorySourceException {
307         if (this.getName() == null || this.getName().trim().length() == 0) {
308             throw new RepositorySourceException(JdbcMetadataI18n.repositorySourceMustHaveName.text());
309         }
310 
311         if (repository == null) {
312             repository = new JdbcMetadataRepository(this);
313 
314             ExecutionContext context = repositoryContext != null ? repositoryContext.getExecutionContext() : defaultContext;
315             JdbcMetadataTransaction txn = repository.startTransaction(context, true);
316             try {
317                 repository.createWorkspace(txn, getDefaultWorkspaceName(), CreateConflictBehavior.DO_NOT_CREATE, null);
318             } finally {
319                 txn.commit();
320             }
321 
322         }
323         return new Connection<PathNode, JdbcMetadataWorkspace>(this, repository);
324     }
325 
326     public Reference getReference() {
327         String className = getClass().getName();
328         String factoryClassName = this.getClass().getName();
329         Reference ref = new Reference(className, factoryClassName, null);
330 
331         ref.add(new StringRefAddr(SOURCE_NAME, getName()));
332         ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuidObject().toString()));
333         ref.add(new StringRefAddr(DATA_SOURCE_JNDI_NAME, getDataSourceJndiName()));
334         ref.add(new StringRefAddr(USERNAME, getUsername()));
335         ref.add(new StringRefAddr(PASSWORD, getPassword()));
336         ref.add(new StringRefAddr(URL, getUrl()));
337         ref.add(new StringRefAddr(DRIVER_CLASS_NAME, getDriverClassName()));
338         ref.add(new StringRefAddr(DRIVER_CLASSLOADER_NAME, getDriverClassloaderName()));
339         ref.add(new StringRefAddr(MAXIMUM_CONNECTIONS_IN_POOL, Integer.toString(getMaximumConnectionsInPool())));
340         ref.add(new StringRefAddr(MINIMUM_CONNECTIONS_IN_POOL, Integer.toString(getMinimumConnectionsInPool())));
341         ref.add(new StringRefAddr(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS,
342                                   Integer.toString(getMaximumConnectionIdleTimeInSeconds())));
343         ref.add(new StringRefAddr(MAXIMUM_SIZE_OF_STATEMENT_CACHE, Integer.toString(getMaximumSizeOfStatementCache())));
344         ref.add(new StringRefAddr(NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED,
345                                   Integer.toString(getNumberOfConnectionsToAcquireAsNeeded())));
346         ref.add(new StringRefAddr(IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS,
347                                   Integer.toString(getIdleTimeInSecondsBeforeTestingConnections())));
348         ref.add(new StringRefAddr(DEFAULT_WORKSPACE, getDefaultWorkspaceName()));
349         ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
350 
351         ref.add(new StringRefAddr(DEFAULT_CATALOG_NAME, getDefaultCatalogName()));
352         ref.add(new StringRefAddr(DEFAULT_SCHEMA_NAME, getDefaultSchemaName()));
353         ref.add(new StringRefAddr(METADATA_COLLECTOR_CLASS_NAME, getMetadataCollectorClassName()));
354 
355         return ref;
356     }
357 
358     public Object getObjectInstance( Object obj,
359                                      javax.naming.Name name,
360                                      Context nameCtx,
361                                      Hashtable<?, ?> environment ) throws Exception {
362         if (!(obj instanceof Reference)) {
363             return null;
364         }
365 
366         Map<String, Object> values = valuesFrom((Reference)obj);
367 
368         String sourceName = (String)values.get(SOURCE_NAME);
369         String rootNodeUuid = (String)values.get(ROOT_NODE_UUID);
370         String dataSourceJndiName = (String)values.get(DATA_SOURCE_JNDI_NAME);
371         String username = (String)values.get(USERNAME);
372         String password = (String)values.get(PASSWORD);
373         String url = (String)values.get(URL);
374         String driverClassName = (String)values.get(DRIVER_CLASS_NAME);
375         String driverClassloaderName = (String)values.get(DRIVER_CLASSLOADER_NAME);
376         String maxConnectionsInPool = (String)values.get(MAXIMUM_CONNECTIONS_IN_POOL);
377         String minConnectionsInPool = (String)values.get(MINIMUM_CONNECTIONS_IN_POOL);
378         String maxConnectionIdleTimeInSec = (String)values.get(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS);
379         String maxSizeOfStatementCache = (String)values.get(MAXIMUM_SIZE_OF_STATEMENT_CACHE);
380         String acquisitionIncrement = (String)values.get(NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED);
381         String idleTimeInSeconds = (String)values.get(IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS);
382         String retryLimit = (String)values.get(RETRY_LIMIT);
383         String defaultWorkspace = (String)values.get(DEFAULT_WORKSPACE);
384         String defaultCatalogName = (String)values.get(DEFAULT_CATALOG_NAME);
385         String defaultSchemaName = (String)values.get(DEFAULT_SCHEMA_NAME);
386         String metadataCollectorClassName = (String)values.get(METADATA_COLLECTOR_CLASS_NAME);
387 
388         // Create the source instance ...
389         JdbcMetadataSource source = new JdbcMetadataSource();
390         if (sourceName != null) source.setName(sourceName);
391         if (rootNodeUuid != null) source.setRootNodeUuidObject(rootNodeUuid);
392         if (dataSourceJndiName != null) source.setDataSourceJndiName(dataSourceJndiName);
393         if (username != null) source.setUsername(username);
394         if (password != null) source.setPassword(password);
395         if (url != null) source.setUrl(url);
396         if (driverClassName != null) source.setDriverClassName(driverClassName);
397         if (driverClassloaderName != null) source.setDriverClassloaderName(driverClassloaderName);
398         if (maxConnectionsInPool != null) source.setMaximumConnectionsInPool(Integer.parseInt(maxConnectionsInPool));
399         if (minConnectionsInPool != null) source.setMinimumConnectionsInPool(Integer.parseInt(minConnectionsInPool));
400         if (maxConnectionIdleTimeInSec != null) source.setMaximumConnectionIdleTimeInSeconds(Integer.parseInt(maxConnectionIdleTimeInSec));
401         if (maxSizeOfStatementCache != null) source.setMaximumSizeOfStatementCache(Integer.parseInt(maxSizeOfStatementCache));
402         if (acquisitionIncrement != null) source.setNumberOfConnectionsToAcquireAsNeeded(Integer.parseInt(acquisitionIncrement));
403         if (idleTimeInSeconds != null) source.setIdleTimeInSecondsBeforeTestingConnections(Integer.parseInt(idleTimeInSeconds));
404         if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
405         if (defaultWorkspace != null) source.setDefaultWorkspaceName(defaultWorkspace);
406         if (defaultCatalogName != null) source.setDefaultCatalogName(defaultCatalogName);
407         if (defaultSchemaName != null) source.setDefaultCatalogName(defaultSchemaName);
408         if (metadataCollectorClassName != null) source.setMetadataCollectorClassName(metadataCollectorClassName);
409 
410         return source;
411 
412     }
413 
414     /**
415      * @return dataSourceJndiName
416      */
417     public String getDataSourceJndiName() {
418         return dataSourceJndiName;
419     }
420 
421     /**
422      * @param dataSourceJndiName Sets dataSourceJndiName to the specified value.
423      */
424     public void setDataSourceJndiName( String dataSourceJndiName ) {
425         if (dataSourceJndiName != null && dataSourceJndiName.trim().length() == 0) dataSourceJndiName = null;
426         this.dataSourceJndiName = dataSourceJndiName;
427     }
428 
429     /**
430      * @return driverClassName
431      */
432     public String getDriverClassName() {
433         return driverClassName;
434     }
435 
436     /**
437      * @param driverClassName Sets driverClassName to the specified value.
438      */
439     public synchronized void setDriverClassName( String driverClassName ) {
440         if (driverClassName != null && driverClassName.trim().length() == 0) driverClassName = null;
441         this.driverClassName = driverClassName;
442     }
443 
444     /**
445      * @return driverClassloaderName
446      */
447     public String getDriverClassloaderName() {
448         return driverClassloaderName;
449     }
450 
451     /**
452      * @param driverClassloaderName Sets driverClassloaderName to the specified value.
453      */
454     public synchronized void setDriverClassloaderName( String driverClassloaderName ) {
455         if (driverClassloaderName != null && driverClassloaderName.trim().length() == 0) driverClassloaderName = null;
456         this.driverClassloaderName = driverClassloaderName;
457     }
458 
459     /**
460      * @return username
461      */
462     public String getUsername() {
463         return username;
464     }
465 
466     /**
467      * @param username Sets username to the specified value.
468      */
469     public synchronized void setUsername( String username ) {
470         this.username = username;
471     }
472 
473     /**
474      * @return password
475      */
476     public String getPassword() {
477         return password;
478     }
479 
480     /**
481      * @param password Sets password to the specified value.
482      */
483     public synchronized void setPassword( String password ) {
484         this.password = password;
485     }
486 
487     /**
488      * @return url
489      */
490     public String getUrl() {
491         return url;
492     }
493 
494     /**
495      * @param url Sets url to the specified value.
496      */
497     public synchronized void setUrl( String url ) {
498         if (url != null && url.trim().length() == 0) url = null;
499         this.url = url;
500     }
501 
502     /**
503      * @return maximumConnectionsInPool
504      */
505     public int getMaximumConnectionsInPool() {
506         return maximumConnectionsInPool;
507     }
508 
509     /**
510      * @param maximumConnectionsInPool Sets maximumConnectionsInPool to the specified value.
511      */
512     public synchronized void setMaximumConnectionsInPool( int maximumConnectionsInPool ) {
513         if (maximumConnectionsInPool < 0) maximumConnectionsInPool = DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL;
514         this.maximumConnectionsInPool = maximumConnectionsInPool;
515     }
516 
517     /**
518      * @return minimumConnectionsInPool
519      */
520     public int getMinimumConnectionsInPool() {
521         return minimumConnectionsInPool;
522     }
523 
524     /**
525      * @param minimumConnectionsInPool Sets minimumConnectionsInPool to the specified value.
526      */
527     public synchronized void setMinimumConnectionsInPool( int minimumConnectionsInPool ) {
528         if (minimumConnectionsInPool < 0) minimumConnectionsInPool = DEFAULT_MINIMUM_CONNECTIONS_IN_POOL;
529         this.minimumConnectionsInPool = minimumConnectionsInPool;
530     }
531 
532     /**
533      * @return maximumConnectionIdleTimeInSeconds
534      */
535     public int getMaximumConnectionIdleTimeInSeconds() {
536         return maximumConnectionIdleTimeInSeconds;
537     }
538 
539     /**
540      * @param maximumConnectionIdleTimeInSeconds Sets maximumConnectionIdleTimeInSeconds to the specified value.
541      */
542     public synchronized void setMaximumConnectionIdleTimeInSeconds( int maximumConnectionIdleTimeInSeconds ) {
543         if (maximumConnectionIdleTimeInSeconds < 0) maximumConnectionIdleTimeInSeconds = DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS;
544         this.maximumConnectionIdleTimeInSeconds = maximumConnectionIdleTimeInSeconds;
545     }
546 
547     /**
548      * @return maximumSizeOfStatementCache
549      */
550     public int getMaximumSizeOfStatementCache() {
551         return maximumSizeOfStatementCache;
552     }
553 
554     /**
555      * @param maximumSizeOfStatementCache Sets maximumSizeOfStatementCache to the specified value.
556      */
557     public synchronized void setMaximumSizeOfStatementCache( int maximumSizeOfStatementCache ) {
558         if (maximumSizeOfStatementCache < 0) maximumSizeOfStatementCache = DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE;
559         this.maximumSizeOfStatementCache = maximumSizeOfStatementCache;
560     }
561 
562     /**
563      * @return numberOfConnectionsToAcquireAsNeeded
564      */
565     public int getNumberOfConnectionsToAcquireAsNeeded() {
566         return numberOfConnectionsToAcquireAsNeeded;
567     }
568 
569     /**
570      * @param numberOfConnectionsToAcquireAsNeeded Sets numberOfConnectionsToAcquireAsNeeded to the specified value.
571      */
572     public synchronized void setNumberOfConnectionsToAcquireAsNeeded( int numberOfConnectionsToAcquireAsNeeded ) {
573         if (numberOfConnectionsToAcquireAsNeeded < 0) numberOfConnectionsToAcquireAsNeeded = DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED;
574         this.numberOfConnectionsToAcquireAsNeeded = numberOfConnectionsToAcquireAsNeeded;
575     }
576 
577     /**
578      * @return idleTimeInSecondsBeforeTestingConnections
579      */
580     public int getIdleTimeInSecondsBeforeTestingConnections() {
581         return idleTimeInSecondsBeforeTestingConnections;
582     }
583 
584     /**
585      * @param idleTimeInSecondsBeforeTestingConnections Sets idleTimeInSecondsBeforeTestingConnections to the specified value.
586      */
587     public synchronized void setIdleTimeInSecondsBeforeTestingConnections( int idleTimeInSecondsBeforeTestingConnections ) {
588         if (idleTimeInSecondsBeforeTestingConnections < 0) idleTimeInSecondsBeforeTestingConnections = DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS;
589         this.idleTimeInSecondsBeforeTestingConnections = idleTimeInSecondsBeforeTestingConnections;
590     }
591 
592     /**
593      * Set the {@link DataSource} instance that this source should use.
594      * 
595      * @param dataSource the data source; may be null
596      * @see #getDataSource()
597      * @see #setDataSourceJndiName(String)
598      */
599     /*package*/synchronized void setDataSource( DataSource dataSource ) {
600         this.dataSource = dataSource;
601     }
602 
603     /**
604      * Get the name of the default workspace.
605      * 
606      * @return the name of the workspace that should be used by default, or null if there is no default workspace
607      */
608     public String getDefaultWorkspaceName() {
609         return defaultWorkspace;
610     }
611 
612     /**
613      * Set the name of the workspace that should be used when clients don't specify a workspace.
614      * 
615      * @param nameOfDefaultWorkspace the name of the workspace that should be used by default, or null if the
616      *        {@link #DEFAULT_NAME_OF_DEFAULT_WORKSPACE default name} should be used
617      */
618     public synchronized void setDefaultWorkspaceName( String nameOfDefaultWorkspace ) {
619         this.defaultWorkspace = nameOfDefaultWorkspace != null ? nameOfDefaultWorkspace : DEFAULT_NAME_OF_DEFAULT_WORKSPACE;
620     }
621 
622     /**
623      * Get the name of the default catalog.
624      * 
625      * @return the name that should be used as the catalog name when the database does not support catalogs
626      */
627     public String getDefaultCatalogName() {
628         return defaultCatalogName;
629     }
630 
631     /**
632      * Set the name of the catalog that should be used when the database does not support catalogs.
633      * 
634      * @param defaultCatalogName the name that should be used as the catalog name by default, or null if the
635      *        {@link #DEFAULT_NAME_OF_DEFAULT_CATALOG default name} should be used
636      */
637     public void setDefaultCatalogName( String defaultCatalogName ) {
638         this.defaultCatalogName = defaultCatalogName == null ? DEFAULT_NAME_OF_DEFAULT_CATALOG : defaultCatalogName;
639     }
640 
641     /**
642      * Get the name of the default schema.
643      * 
644      * @return the name that should be used as the schema name when the database does not support schemas
645      */
646     public String getDefaultSchemaName() {
647         return defaultSchemaName;
648     }
649 
650     /**
651      * Set the name of the schema that should be used when the database does not support schemas.
652      * 
653      * @param defaultSchemaName the name that should be used as the schema name by default, or null if the
654      *        {@link #DEFAULT_NAME_OF_DEFAULT_SCHEMA default name} should be used
655      */
656     public void setDefaultSchemaName( String defaultSchemaName ) {
657         this.defaultSchemaName = defaultSchemaName == null ? DEFAULT_NAME_OF_DEFAULT_SCHEMA : defaultSchemaName;
658     }
659 
660     /**
661      * Get the class name of the metadata collector.
662      * 
663      * @return the name the class name of the metadata collector
664      */
665     public String getMetadataCollectorClassName() {
666         return metadataCollectorClassName;
667     }
668 
669     /**
670      * Set the class name of the metadata collector and instantiates a new metadata collector object for that class
671      * 
672      * @param metadataCollectorClassName the class name for the metadata collector, or null if the
673      *        {@link #DEFAULT_METADATA_COLLECTOR default metadata collector} should be used
674      * @throws ClassNotFoundException if the the named metadata collector class cannot be located
675      * @throws IllegalAccessException if the metadata collector class or its nullary constructor is not accessible.
676      * @throws InstantiationException if the metadata collector class represents an abstract class, an interface, an array class,
677      *         a primitive type, or void; or if the class has no nullary constructor; or if the instantiation fails for some other
678      *         reason.
679      * @throws ClassCastException if the given class cannot be cast to {@link MetadataCollector}.
680      * @see Class#forName(String)
681      * @see Class#newInstance()
682      */
683     @SuppressWarnings( "unchecked" )
684     public synchronized void setMetadataCollectorClassName( String metadataCollectorClassName )
685         throws ClassNotFoundException, IllegalAccessException, InstantiationException {
686         if (metadataCollectorClassName == null) {
687             this.metadataCollectorClassName = DEFAULT_METADATA_COLLECTOR.getClass().getName();
688             this.metadataCollector = DEFAULT_METADATA_COLLECTOR;
689         } else {
690             Class newCollectorClass = Class.forName(metadataCollectorClassName);
691             this.metadataCollector = (MetadataCollector)newCollectorClass.newInstance();
692             this.metadataCollectorClassName = metadataCollectorClassName;
693         }
694     }
695 
696     /**
697      * Returns the metadata collector instance
698      * 
699      * @return the metadata collector
700      */
701     public synchronized MetadataCollector getMetadataCollector() {
702         return metadataCollector;
703     }
704 
705     /**
706      * In-memory connectors aren't shared and cannot be loaded from external sources if updates are not allowed. Therefore, in
707      * order to avoid setting up an in-memory connector that is permanently empty (presumably, not a desired outcome), all
708      * in-memory connectors must allow updates.
709      * 
710      * @param updatesAllowed must be true
711      * @throws RepositorySourceException if {@code updatesAllowed != true}.
712      */
713     public void setUpdatesAllowed( boolean updatesAllowed ) {
714         if (updatesAllowed == false) {
715             throw new RepositorySourceException(JdbcMetadataI18n.sourceIsReadOnly.text(this.getName()));
716         }
717 
718     }
719 
720 }