/*******************************************************************************
* Copyright (c) 2008 Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source$
* Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
* Created on: Jul 18, 2008
* Revision: $Id$
*
* Contributors:
* Cambridge Semantics Incorporated - initial API and implementation
*******************************************************************************/
package org.openanzo.client.pool;
import java.util.Dictionary;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.pool.PoolableObjectFactory;
import org.openanzo.client.AnzoClient;
import org.openanzo.combus.IJmsProvider;
import org.openanzo.datasource.DatasourceDictionary;
import org.openanzo.datasource.IDatasource;
import org.openanzo.datasource.IDatasourceListener;
import org.openanzo.datasource.IDatasourceRegistrationListener;
import org.openanzo.datasource.NamedDatasourceTracker;
import org.openanzo.datasource.PrimaryDatasourceTracker;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.exceptions.Messages;
import org.openanzo.osgi.IStatusProvider;
import org.openanzo.osgi.ServiceLifecycleState;
import org.openanzo.services.IAuthenticationService;
import org.openanzo.services.IExecutionService;
import org.openanzo.services.IUpdatesProvider;
import org.openanzo.services.ServicesDictionary;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Pool of anzo clients
*
* @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>)
*
*/
public class AnzoClientPool implements IStatusProvider {
private static final Logger log = LoggerFactory.getLogger(AnzoClientPool.class);
private long jmsCount = 0;
private long nonjmsCount = 0;
private final List<AnzoClient> anzoClients = new CopyOnWriteArrayList<AnzoClient>();
private IDatasource datasource = null;
private Set<IDatasourceListener> datasourceListeners = new CopyOnWriteArraySet<IDatasourceListener>();
private ServiceTracker datasourceTracker = null;
private IAuthenticationService authenticationService = null;
private IExecutionService executionService = null;
private IJmsProvider jmsProvider = null;
private IUpdatesProvider updatesProvider = null;
private Dictionary<? extends Object, ? extends Object> configProperties = null;
private BundleContext context = null;
private ServiceRegistration reg = null;
protected ReentrantLock lock = new ReentrantLock();
protected ServiceLifecycleState state = ServiceLifecycleState.CREATED;
String instance = null;
/**
* Create a new anzo client pool
*
* @param configProperties
* configuration properties
* @param context
* bundle context
* @param authenticationService
* the authentication service
* @param executionService
* the execution service
* @param updatesProvider
* updates provider
* @param jmsProvider
* jmsprovider
*/
public AnzoClientPool(Dictionary<? extends Object, ? extends Object> configProperties, BundleContext context, IAuthenticationService authenticationService, IExecutionService executionService, IUpdatesProvider updatesProvider, IJmsProvider jmsProvider) {
this.context = context;
this.configProperties = configProperties;
this.authenticationService = authenticationService;
this.executionService = executionService;
this.jmsProvider = jmsProvider;
this.updatesProvider = updatesProvider;
String datasourceURI = DatasourceDictionary.getDatasourceURI(configProperties);
if (datasourceURI == null) {
try {
datasourceTracker = new PrimaryDatasourceTracker(context, new IDatasourceRegistrationListener() {
public void unregisterDatasource(IDatasource datasource) {
AnzoClientPool.this.datasource = null;
}
public void registerDatasource(IDatasource datasource) {
AnzoClientPool.this.datasource = datasource;
start();
}
});
} catch (InvalidSyntaxException ise) {
log.error(LogUtils.SERVER_INTERNAL_MARKER, Messages.formatString(ExceptionConstants.OSGI.INVALID_SERVICE_SYNTAX, ise.getFilter()), ise);
}
} else {
try {
datasourceTracker = new NamedDatasourceTracker(context, datasourceURI, new IDatasourceRegistrationListener() {
public void unregisterDatasource(IDatasource datasource) {
AnzoClientPool.this.datasource = null;
}
public void registerDatasource(IDatasource datasource) {
AnzoClientPool.this.datasource = datasource;
start();
}
});
} catch (InvalidSyntaxException ise) {
log.error(LogUtils.SERVER_INTERNAL_MARKER, Messages.formatString(ExceptionConstants.OSGI.INVALID_SERVICE_SYNTAX, ise.getFilter()), ise);
}
}
datasourceTracker.open();
instance = ServicesDictionary.getInstanceURI(configProperties);
}
void start() {
lock.lock();
try {
if (state != ServiceLifecycleState.STARTED) {
datasource.registerDatasourceListener(new IDatasourceListener() {
public void resetStarting() throws AnzoException {
for (IDatasourceListener listener : datasourceListeners) {
listener.resetStarting();
}
for (AnzoClient client : anzoClients) {
client.clear();
}
}
public void reset() throws AnzoException {
for (IDatasourceListener listener : datasourceListeners) {
listener.reset();
}
}
public void postReset() throws AnzoException {
for (IDatasourceListener listener : datasourceListeners) {
listener.postReset();
}
}
public void resetFinished() throws AnzoException {
for (IDatasourceListener listener : datasourceListeners) {
listener.resetFinished();
}
}
});
}
} finally {
lock.unlock();
}
//fixes #873
reg = context.registerService(AnzoClientPool.class.getName(), this, configProperties);
state = ServiceLifecycleState.STARTED;
}
/**
* Register an {@link IDatasourceListener}
*
* @param listener
* listener to register
*/
public void registerDatasourceListener(IDatasourceListener listener) {
datasourceListeners.add(listener);
}
/**
* Unregister an {@link IDatasourceListener}
*
* @param listener
* listener to unregister
*/
public void unregisterDatasourceListener(IDatasourceListener listener) {
datasourceListeners.remove(listener);
}
/**
* Get anzo client from the pool
*
* @param useJms
* should the client use jms
* @param userDescription
* description of user
* @return anzo client from the pool
* @throws AnzoException
*/
public RestrictedAnzoClient getAnzoClient(boolean useJms, String userDescription) throws AnzoException {
if (context.getBundle().getState() != Bundle.ACTIVE) {
throw new AnzoException(ExceptionConstants.OSGI.BUNDLE_NOT_ACTIVE, context.getBundle().getSymbolicName(), Integer.toString(context.getBundle().getState()));
}
lock.lock();
try {
RestrictedAnzoClient anzoClient = new RestrictedAnzoClient(configProperties, datasource, authenticationService, executionService, updatesProvider, jmsProvider, useJms);
anzoClients.add(anzoClient);
anzoClient.setUserDescription(userDescription);
anzoClient.connect();
if (useJms)
jmsCount++;
else
nonjmsCount--;
return anzoClient;
} catch (AnzoException exception) {
log.error(LogUtils.SERVER_INTERNAL_MARKER, Messages.formatString(ExceptionConstants.SERVER.ERROR_GETTING_FROM_POOL), exception);
throw exception;
} finally {
lock.unlock();
}
}
/**
* Return anzo client to the pool
*
* @param client
* client to return to pool
*/
public void returnAnzoClient(RestrictedAnzoClient client) {
returnAnzoClient(client, true);
}
/**
* Return anzo client to the pool
*
* @param client
* client to return to pool
* @param closeJms
* if true, execute full jms close protocols, false if jms is already closed
*/
public void returnAnzoClient(RestrictedAnzoClient client, boolean closeJms) {
lock.lock();
try {
client.restrictedClose(closeJms);
anzoClients.remove(client);
if (client.useJms) {
jmsCount--;
} else {
nonjmsCount--;
}
} finally {
lock.unlock();
}
}
/**
* Close the client pool
*
* @param bundleStopping
* was the entire OSGI system stopped, or just this bundle
* @throws AnzoException
*/
public void close(boolean bundleStopping) throws AnzoException {
lock.lock();
try {
if (state != ServiceLifecycleState.STOPPED) {
if (!bundleStopping && reg != null) {
reg.unregister();
}
if (datasourceTracker != null) {
datasourceTracker.close();
}
state = ServiceLifecycleState.STOPPED;
}
} finally {
lock.unlock();
}
}
class AnzoClientFactory implements PoolableObjectFactory {
final boolean useJms;
public AnzoClientFactory(boolean useJms) {
this.useJms = useJms;
}
public Object makeObject() throws Exception {
RestrictedAnzoClient anzoClient = new RestrictedAnzoClient(configProperties, datasource, authenticationService, executionService, updatesProvider, jmsProvider, useJms);
anzoClients.add(anzoClient);
return anzoClient;
}
public void destroyObject(Object object) throws Exception {
RestrictedAnzoClient client = (RestrictedAnzoClient) object;
client.restrictedClose();
anzoClients.remove(client);
}
public void passivateObject(Object object) throws Exception {
RestrictedAnzoClient client = (RestrictedAnzoClient) object;
client.disconnect();
client.clear();
client.setServiceUser(null);
}
public void activateObject(Object arg0) throws Exception {
}
public boolean validateObject(Object arg0) {
return true;
}
}
public String getCurrentStatus(boolean html) {
return instance;
}
public ServiceLifecycleState getState() {
return state;
}
}