InstanceKeyDataSourceFactory.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.commons.dbcp2.datasources;
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.time.Duration;
- import java.util.ArrayList;
- import java.util.Hashtable;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.concurrent.ConcurrentHashMap;
- import javax.naming.Context;
- import javax.naming.Name;
- import javax.naming.RefAddr;
- import javax.naming.Reference;
- import javax.naming.spi.ObjectFactory;
- import org.apache.commons.dbcp2.ListException;
- import org.apache.commons.dbcp2.Utils;
- /**
- * A JNDI ObjectFactory which creates {@code SharedPoolDataSource}s or {@code PerUserPoolDataSource}s
- *
- * @since 2.0
- */
- abstract class InstanceKeyDataSourceFactory implements ObjectFactory {
- private static final Map<String, InstanceKeyDataSource> INSTANCE_MAP = new ConcurrentHashMap<>();
- /**
- * Closes all pools associated with this class.
- *
- * @throws ListException
- * a {@link ListException} containing all exceptions thrown by {@link InstanceKeyDataSource#close()}
- * @see InstanceKeyDataSource#close()
- * @since 2.4.0 throws a {@link ListException} instead of, in 2.3.0 and before, the first exception thrown by
- * {@link InstanceKeyDataSource#close()}.
- */
- public static void closeAll() throws ListException {
- // Get iterator to loop over all instances of this data source.
- final List<Throwable> exceptionList = new ArrayList<>(INSTANCE_MAP.size());
- INSTANCE_MAP.entrySet().forEach(entry -> {
- // Bullet-proof to avoid anything else but problems from InstanceKeyDataSource#close().
- if (entry != null) {
- @SuppressWarnings("resource")
- final InstanceKeyDataSource value = entry.getValue();
- Utils.close(value, exceptionList::add);
- }
- });
- INSTANCE_MAP.clear();
- if (!exceptionList.isEmpty()) {
- throw new ListException("Could not close all InstanceKeyDataSource instances.", exceptionList);
- }
- }
- /**
- * Deserializes the provided byte array to create an object.
- *
- * @param data
- * Data to deserialize to create the configuration parameter.
- *
- * @return The Object created by deserializing the data.
- *
- * @throws ClassNotFoundException
- * If a class cannot be found during the deserialization of a configuration parameter.
- * @throws IOException
- * If an I/O error occurs during the deserialization of a configuration parameter.
- */
- protected static final Object deserialize(final byte[] data) throws IOException, ClassNotFoundException {
- ObjectInputStream in = null;
- try {
- in = new ObjectInputStream(new ByteArrayInputStream(data));
- return in.readObject();
- } finally {
- Utils.closeQuietly(in);
- }
- }
- static synchronized String registerNewInstance(final InstanceKeyDataSource ds) {
- int max = 0;
- for (final String s : INSTANCE_MAP.keySet()) {
- if (s != null) {
- try {
- max = Math.max(max, Integer.parseInt(s));
- } catch (final NumberFormatException ignored) {
- // no sweat, ignore those keys
- }
- }
- }
- final String instanceKey = String.valueOf(max + 1);
- // Put a placeholder here for now, so other instances will not
- // take our key. We will replace with a pool when ready.
- INSTANCE_MAP.put(instanceKey, ds);
- return instanceKey;
- }
- static void removeInstance(final String key) {
- if (key != null) {
- INSTANCE_MAP.remove(key);
- }
- }
- private Boolean booleanValueOf(RefAddr refAddr) {
- return Boolean.valueOf(toString(refAddr));
- }
- /**
- * Creates an instance of the subclass and sets any properties contained in the Reference.
- *
- * @param ref
- * The properties to be set on the created DataSource
- *
- * @return A configured DataSource of the appropriate type.
- *
- * @throws ClassNotFoundException
- * If a class cannot be found during the deserialization of a configuration parameter.
- * @throws IOException
- * If an I/O error occurs during the deserialization of a configuration parameter.
- */
- protected abstract InstanceKeyDataSource getNewInstance(Reference ref) throws IOException, ClassNotFoundException;
- /**
- * Implements ObjectFactory to create an instance of SharedPoolDataSource or PerUserPoolDataSource
- */
- @Override
- public Object getObjectInstance(final Object refObj, final Name name, final Context context,
- final Hashtable<?, ?> env) throws IOException, ClassNotFoundException {
- // The spec says to return null if we can't create an instance
- // of the reference
- Object obj = null;
- if (refObj instanceof Reference) {
- final Reference ref = (Reference) refObj;
- if (isCorrectClass(ref.getClassName())) {
- final RefAddr refAddr = ref.get("instanceKey");
- if (hasContent(refAddr)) {
- // object was bound to JNDI via Referenceable API.
- obj = INSTANCE_MAP.get(refAddr.getContent());
- } else {
- // Tomcat JNDI creates a Reference out of server.xml
- // <ResourceParam> configuration and passes it to an
- // instance of the factory given in server.xml.
- String key = null;
- if (name != null) {
- key = name.toString();
- obj = INSTANCE_MAP.get(key);
- }
- if (obj == null) {
- final InstanceKeyDataSource ds = getNewInstance(ref);
- setCommonProperties(ref, ds);
- obj = ds;
- if (key != null) {
- INSTANCE_MAP.put(key, ds);
- }
- }
- }
- }
- }
- return obj;
- }
- private boolean hasContent(final RefAddr refAddr) {
- return refAddr != null && refAddr.getContent() != null;
- }
- /**
- * Tests if className is the value returned from getClass().getName().toString().
- *
- * @param className
- * The class name to test.
- *
- * @return true if and only if className is the value returned from getClass().getName().toString()
- */
- protected abstract boolean isCorrectClass(String className);
- boolean parseBoolean(final RefAddr refAddr) {
- return Boolean.parseBoolean(toString(refAddr));
- }
- int parseInt(final RefAddr refAddr) {
- return Integer.parseInt(toString(refAddr));
- }
- long parseLong(final RefAddr refAddr) {
- return Long.parseLong(toString(refAddr));
- }
- private void setCommonProperties(final Reference ref, final InstanceKeyDataSource ikds)
- throws IOException, ClassNotFoundException {
- RefAddr refAddr = ref.get("dataSourceName");
- if (hasContent(refAddr)) {
- ikds.setDataSourceName(toString(refAddr));
- }
- refAddr = ref.get("description");
- if (hasContent(refAddr)) {
- ikds.setDescription(toString(refAddr));
- }
- refAddr = ref.get("jndiEnvironment");
- if (hasContent(refAddr)) {
- final byte[] serialized = (byte[]) refAddr.getContent();
- ikds.setJndiEnvironment((Properties) deserialize(serialized));
- }
- refAddr = ref.get("loginTimeout");
- if (hasContent(refAddr)) {
- ikds.setLoginTimeout(toDurationFromSeconds(refAddr));
- }
- // Pool properties
- refAddr = ref.get("blockWhenExhausted");
- if (hasContent(refAddr)) {
- ikds.setDefaultBlockWhenExhausted(parseBoolean(refAddr));
- }
- refAddr = ref.get("evictionPolicyClassName");
- if (hasContent(refAddr)) {
- ikds.setDefaultEvictionPolicyClassName(toString(refAddr));
- }
- // Pool properties
- refAddr = ref.get("lifo");
- if (hasContent(refAddr)) {
- ikds.setDefaultLifo(parseBoolean(refAddr));
- }
- refAddr = ref.get("maxIdlePerKey");
- if (hasContent(refAddr)) {
- ikds.setDefaultMaxIdle(parseInt(refAddr));
- }
- refAddr = ref.get("maxTotalPerKey");
- if (hasContent(refAddr)) {
- ikds.setDefaultMaxTotal(parseInt(refAddr));
- }
- refAddr = ref.get("maxWaitMillis");
- if (hasContent(refAddr)) {
- ikds.setDefaultMaxWait(toDurationFromMillis(refAddr));
- }
- refAddr = ref.get("minEvictableIdleTimeMillis");
- if (hasContent(refAddr)) {
- ikds.setDefaultMinEvictableIdle(toDurationFromMillis(refAddr));
- }
- refAddr = ref.get("minIdlePerKey");
- if (hasContent(refAddr)) {
- ikds.setDefaultMinIdle(parseInt(refAddr));
- }
- refAddr = ref.get("numTestsPerEvictionRun");
- if (hasContent(refAddr)) {
- ikds.setDefaultNumTestsPerEvictionRun(parseInt(refAddr));
- }
- refAddr = ref.get("softMinEvictableIdleTimeMillis");
- if (hasContent(refAddr)) {
- ikds.setDefaultSoftMinEvictableIdle(toDurationFromMillis(refAddr));
- }
- refAddr = ref.get("testOnCreate");
- if (hasContent(refAddr)) {
- ikds.setDefaultTestOnCreate(parseBoolean(refAddr));
- }
- refAddr = ref.get("testOnBorrow");
- if (hasContent(refAddr)) {
- ikds.setDefaultTestOnBorrow(parseBoolean(refAddr));
- }
- refAddr = ref.get("testOnReturn");
- if (hasContent(refAddr)) {
- ikds.setDefaultTestOnReturn(parseBoolean(refAddr));
- }
- refAddr = ref.get("testWhileIdle");
- if (hasContent(refAddr)) {
- ikds.setDefaultTestWhileIdle(parseBoolean(refAddr));
- }
- refAddr = ref.get("timeBetweenEvictionRunsMillis");
- if (hasContent(refAddr)) {
- ikds.setDefaultDurationBetweenEvictionRuns(toDurationFromMillis(refAddr));
- }
- // Connection factory properties
- refAddr = ref.get("validationQuery");
- if (hasContent(refAddr)) {
- ikds.setValidationQuery(toString(refAddr));
- }
- refAddr = ref.get("validationQueryTimeout");
- if (hasContent(refAddr)) {
- ikds.setValidationQueryTimeout(toDurationFromSeconds(refAddr));
- }
- refAddr = ref.get("rollbackAfterValidation");
- if (hasContent(refAddr)) {
- ikds.setRollbackAfterValidation(parseBoolean(refAddr));
- }
- refAddr = ref.get("maxConnLifetimeMillis");
- if (hasContent(refAddr)) {
- ikds.setMaxConnLifetime(toDurationFromMillis(refAddr));
- }
- // Connection properties
- refAddr = ref.get("defaultAutoCommit");
- if (hasContent(refAddr)) {
- ikds.setDefaultAutoCommit(booleanValueOf(refAddr));
- }
- refAddr = ref.get("defaultTransactionIsolation");
- if (hasContent(refAddr)) {
- ikds.setDefaultTransactionIsolation(parseInt(refAddr));
- }
- refAddr = ref.get("defaultReadOnly");
- if (hasContent(refAddr)) {
- ikds.setDefaultReadOnly(booleanValueOf(refAddr));
- }
- }
- private Duration toDurationFromMillis(RefAddr refAddr) {
- return Duration.ofMillis(parseLong(refAddr));
- }
- private Duration toDurationFromSeconds(RefAddr refAddr) {
- return Duration.ofSeconds(parseInt(refAddr));
- }
- String toString(final RefAddr refAddr) {
- return refAddr.getContent().toString();
- }
- }