/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013 ForgeRock AS. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*/
package org.identityconnectors.framework.impl.api;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.identityconnectors.common.Pair;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.framework.api.APIConfiguration;
import org.identityconnectors.framework.api.ConnectorFacade;
import org.identityconnectors.framework.api.ConnectorInfo;
import org.identityconnectors.framework.impl.api.local.LocalConnectorFacadeImpl;
public class ManagedConnectorFacadeFactoryImpl extends ConnectorFacadeFactoryImpl {
private static final Log logger = Log.getLog(ManagedConnectorFacadeFactoryImpl.class);
/**
* Cache of the various ConnectorFacades.
*/
private static final ConcurrentMap<String, Pair<AtomicLong, ConnectorFacade>> CACHE =
new ConcurrentHashMap<String, Pair<AtomicLong, ConnectorFacade>>();
/**
* {@inheritDoc}
*/
@Override
public ConnectorFacade newInstance(final APIConfiguration config) {
ConnectorFacade facade = super.newInstance(config);
Pair<AtomicLong, ConnectorFacade> ret =
CACHE.putIfAbsent(facade.getConnectorFacadeKey(), Pair.of(new AtomicLong(System
.currentTimeMillis()), facade));
if (null != ret) {
logger.ok("ConnectorFacade found in cache");
ret.getKey().set(System.currentTimeMillis());
facade = ret.getValue();
}
return facade;
}
@Override
public ConnectorFacade newInstance(final ConnectorInfo connectorInfo, String config) {
Pair<AtomicLong, ConnectorFacade> facade = CACHE.get(config);
if (null == facade) {
// new ConnectorFacade creation must remain cheap operation
facade =
Pair.of(new AtomicLong(System.currentTimeMillis()), super.newInstance(
connectorInfo, config));
Pair<AtomicLong, ConnectorFacade> ret =
CACHE.putIfAbsent(facade.getValue().getConnectorFacadeKey(), facade);
if (null != ret) {
logger.ok("ConnectorFacade found in cache");
ret.getKey().set(System.currentTimeMillis());
facade = ret;
}
} else {
facade.getKey().set(System.currentTimeMillis());
}
return facade.getValue();
}
/**
* Dispose of all object pools and other resources associated with this
* class.
*/
@Override
public void dispose() {
super.dispose();
for (Pair<AtomicLong, ConnectorFacade> facade : CACHE.values()) {
if (facade.getValue() instanceof LocalConnectorFacadeImpl) {
try {
((LocalConnectorFacadeImpl) facade.getValue()).dispose();
} catch (Exception e) {
logger.warn(e, "Failed to dispose facade: {0}", facade.getValue());
}
}
}
CACHE.clear();
}
public void evictIdle(long time, TimeUnit unit) {
if (unit == null)
throw new NullPointerException();
long lastTime = System.currentTimeMillis() - unit.toMillis(time);
for (Map.Entry<String, Pair<AtomicLong, ConnectorFacade>> entry : CACHE.entrySet()) {
if (entry.getValue().getKey().get() < lastTime) {
if (CACHE.remove(entry.getKey(), entry.getValue())) {
if (entry.getValue().getValue() instanceof LocalConnectorFacadeImpl) {
try {
((LocalConnectorFacadeImpl) entry.getValue().getValue()).dispose();
logger.ok("Disposed managed facade: {0}", entry.getValue());
} catch (Exception e) {
logger.warn(e, "Failed to dispose facade: {0}", entry.getValue());
}
}
}
}
}
}
/**
* Finds the {@code ConnectorFacade} in the cache.
*
* This is used for testing only.
*
* @param facadeKey
* the key to find the {@code ConnectorFacade}.
* @return The {@code ConnectorFacade} or {@code null} if not found.
*/
public ConnectorFacade find(String facadeKey) {
Pair<AtomicLong, ConnectorFacade> pair = CACHE.get(facadeKey);
if (pair != null) {
return pair.getValue();
}
return null;
}
}