/*
* ====================
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License("CDDL") (the "License"). You may not use this file
* except in compliance with the License.
*
* You can obtain a copy of the License at
* http://opensource.org/licenses/cddl1.php
* See the License for the specific language governing permissions and limitations
* under the License.
*
* When distributing the Covered Code, include this CDDL Header Notice in each file
* and include the License file at http://opensource.org/licenses/cddl1.php.
* If applicable, add the following below this CDDL Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
* ====================
* Portions Copyrighted 2010-2013 ForgeRock AS.
*/
package org.identityconnectors.framework.impl.api.local;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.identityconnectors.common.Pair;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.pooling.ObjectPoolConfiguration;
import org.identityconnectors.framework.api.ConnectorKey;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.serializer.SerializerUtil;
import org.identityconnectors.framework.impl.api.APIConfigurationImpl;
import org.identityconnectors.framework.impl.api.ConfigurationPropertiesImpl;
import org.identityconnectors.framework.impl.api.local.operations.OperationalContext;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.Connector;
import org.identityconnectors.framework.spi.PoolableConnector;
public class ConnectorPoolManager {
public static class ConnectorPoolKey {
private final ConnectorKey connectorKey;
private final ConfigurationPropertiesImpl configProperties;
private final ObjectPoolConfiguration poolingConfig;
public ConnectorPoolKey(final ConnectorKey connectorKey,
final ConfigurationPropertiesImpl configProperties,
final ObjectPoolConfiguration poolingConfig) {
this.connectorKey = connectorKey;
this.configProperties = configProperties;
this.poolingConfig = poolingConfig;
}
@Override
public int hashCode() {
return connectorKey.hashCode();
}
@Override
public boolean equals(final Object object) {
if (object instanceof ConnectorPoolKey) {
final ConnectorPoolKey other = (ConnectorPoolKey) object;
if (!connectorKey.equals(other.connectorKey)) {
return false;
}
if (!configProperties.equals(other.configProperties)) {
return false;
}
if (!poolingConfig.equals(other.poolingConfig)) {
return false;
}
return true;
}
return false;
}
}
private static class ConnectorPoolHandler implements ObjectPoolHandler<PoolableConnector> {
private final APIConfigurationImpl apiConfiguration;
private final LocalConnectorInfoImpl localConnectorInfo;
private final OperationalContext context;
public ConnectorPoolHandler(final APIConfigurationImpl apiConfiguration,
final LocalConnectorInfoImpl localInfo) {
this.apiConfiguration = apiConfiguration;
localConnectorInfo = localInfo;
if (localConnectorInfo.isConfigurationStateless()) {
context = null;
} else {
this.context = new OperationalContext(localInfo, apiConfiguration);
}
}
public ObjectPoolConfiguration validate(ObjectPoolConfiguration original) {
ObjectPoolConfiguration configuration =
(ObjectPoolConfiguration) SerializerUtil.cloneObject(original);
configuration.validate();
return configuration;
}
public PoolableConnector makeObject() {
// setup classloader for constructor and
// initialization of config bean and connector
ThreadClassLoaderManager.getInstance().pushClassLoader(
localConnectorInfo.getConnectorClass().getClassLoader());
try {
Class<? extends Connector> clazz = localConnectorInfo.getConnectorClass();
PoolableConnector connector = null;
if (PoolableConnector.class.isAssignableFrom(clazz)) {
Configuration config = null;
if (null == context) {
config =
JavaClassProperties.createBean(apiConfiguration
.getConfigurationProperties(), localConnectorInfo
.getConnectorConfigurationClass());
} else {
config = context.getConfiguration();
}
connector = (PoolableConnector) clazz.newInstance();
connector.init(config);
} else {
throw new ConnectorException("The Connector is not PoolableConnector: "
+ localConnectorInfo.getConnectorKey());
}
return connector;
} catch (Exception e) {
throw ConnectorException.wrap(e);
} finally {
ThreadClassLoaderManager.getInstance().popClassLoader();
}
}
public void testObject(PoolableConnector object) {
ThreadClassLoaderManager.getInstance().pushClassLoader(
localConnectorInfo.getConnectorClass().getClassLoader());
try {
object.checkAlive();
} finally {
ThreadClassLoaderManager.getInstance().popClassLoader();
}
}
public void disposeObject(PoolableConnector object) {
ThreadClassLoaderManager.getInstance().pushClassLoader(
localConnectorInfo.getConnectorClass().getClassLoader());
try {
object.dispose();
} finally {
ThreadClassLoaderManager.getInstance().popClassLoader();
}
}
public void shutdown() {
if (null != context) {
context.dispose();
}
}
}
/**
* Cache of the various POOLS..
*/
private static final ConcurrentMap<ConnectorPoolKey, ObjectPool<PoolableConnector>> POOLS =
new ConcurrentHashMap<ConnectorPoolKey, ObjectPool<PoolableConnector>>();
private static final Log LOG = Log.getLog(ConnectorPoolManager.class);
/**
* Get a object pool for this connector if it supports connector pooling.
*/
public static Pair<ConnectorPoolKey, ObjectPool<PoolableConnector>> getPool(
final APIConfigurationImpl impl, final LocalConnectorInfoImpl localInfo) {
try {
return getPool2(impl, localInfo);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
/**
* Get a object pool for this connector if it was created before.
*/
public static ObjectPool<PoolableConnector> getPool(final ConnectorPoolKey connectorPoolKey) {
return POOLS.get(connectorPoolKey);
}
private static Pair<ConnectorPoolKey, ObjectPool<PoolableConnector>> getPool2(
final APIConfigurationImpl impl, final LocalConnectorInfoImpl localInfo)
throws InstantiationException, IllegalAccessException {
// determine if this connector wants generic connector pooling..
if (impl.isConnectorPoolingSupported()) {
ConnectorPoolKey key =
new ConnectorPoolKey(impl.getConnectorInfo().getConnectorKey(), impl
.getConfigurationProperties(), impl.getConnectorPoolConfiguration());
// get the pool associated..
ObjectPool<PoolableConnector> pool = POOLS.get(key);
// create a new pool if it doesn't exist..
if (pool == null) {
LOG.info("Creating new pool: {0}", impl.getConnectorInfo().getConnectorKey());
// this instance is strictly used for the pool..
pool =
new ObjectPool<PoolableConnector>(
new ConnectorPoolHandler(impl, localInfo), impl
.getConnectorPoolConfiguration());
// add back to the map of POOLS..
ObjectPool<PoolableConnector> previousPool = POOLS.putIfAbsent(key, pool);
// Use the pool made by other thread
if (previousPool != null) {
pool = previousPool;
}
}
return Pair.of(key, pool);
} else if (!localInfo.isConfigurationStateless()) {
return Pair.of(new ConnectorPoolKey(impl.getConnectorInfo().getConnectorKey(), impl
.getConfigurationProperties(), impl.getConnectorPoolConfiguration()), null);
}
return Pair.of(null, null);
}
public static void dispose(final ConnectorPoolKey connectorPoolKey) {
synchronized (POOLS) {
ObjectPool<PoolableConnector> pool = POOLS.remove(connectorPoolKey);
if (null != pool) {
try {
pool.shutdown();
} catch (Exception e) {
LOG.warn(e, "Failed to close pool: {0}", pool);
}
}
}
}
public static void dispose() {
synchronized (POOLS) {
// close each pool..
for (ObjectPool<PoolableConnector> pool : POOLS.values()) {
try {
pool.shutdown();
} catch (Exception e) {
LOG.warn(e, "Failed to close pool: {0}", pool);
}
}
// clear the map of all POOLS..
POOLS.clear();
}
}
}