/*******************************************************************************
* Copyright (c) 2007-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
*
* Contributors:
* Cambridge Semantics Incorporated
*******************************************************************************/
package org.openanzo.execution.java;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.openanzo.client.AnzoClient;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.execution.BaseServiceExecutor;
import org.openanzo.execution.IExecutionContext;
import org.openanzo.ontologies.execution.JavaSemanticService;
import org.openanzo.ontologies.execution.SemanticOperation;
import org.openanzo.ontologies.execution.SemanticService;
import org.openanzo.ontologies.execution.SemanticServiceFactory;
import org.openanzo.ontologies.execution.StateStyleEnum;
import org.openanzo.osgi.registry.RegistryDataset;
import org.openanzo.rdf.Constants;
import org.openanzo.rdf.IAnzoGraph;
import org.openanzo.rdf.IDataset;
import org.openanzo.rdf.Statement;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.utils.AnzoMultiMap;
import org.openanzo.services.ACLUtil;
import org.openanzo.services.DynamicServiceStats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Java Service Executor
*
* @author Ben Szekely ( <a href="mailto:ben@cambridgesemantics.com">ben@cambridgesemantics.com </a>)
*
*/
public class JavaServiceExecutor extends BaseServiceExecutor {
private static final Logger log = LoggerFactory.getLogger(JavaServiceExecutor.class);
private static final String ENABLE_STATS = "enableStats";
private static final String DISABLE_STATS = "disableStats";
// map from service URI to service implementation
private Map<URI, ISemanticService> services = new HashMap<URI, ISemanticService>();
// map from service factory PID to the factory
private Map<String, ISemanticServiceFactory> serviceFactories = new HashMap<String, ISemanticServiceFactory>();
private AnzoMultiMap<String, JavaSemanticService> factoryServices = new AnzoMultiMap<String, JavaSemanticService>();
void registerService(IBundledSemanticService service, IDataset serviceRegistry) throws AnzoException {
services.put(service.getServiceUri(), service);
URI serviceUri = service.getServiceUri();
if (serviceRegistry.containsNamedGraph(serviceUri)) {
log.info(LogUtils.LIFECYCLE_MARKER, "Do not need to update registry for bundled service because it already exists in registry: " + serviceUri);
if (serviceLoaded(serviceUri)) {
log.info(LogUtils.LIFECYCLE_MARKER, "Service already loaded: " + serviceUri);
} else {
JavaSemanticService javaService = SemanticServiceFactory.getJavaSemanticService(serviceUri, serviceRegistry);
loadService(javaService);
}
return;
} else {
IAnzoGraph graph = (IAnzoGraph) serviceRegistry.addNamedGraph(serviceUri);
if (service.getRestrictInitialPermission()) {
ACLUtil.setReadPermission(graph, Constants.NOONE_ROLE, true);
ACLUtil.setAddPermission(graph, Constants.NOONE_ROLE, true);
ACLUtil.setRemovePermission(graph, Constants.NOONE_ROLE, true);
ACLUtil.setReadMetadataPermission(graph, Constants.NOONE_ROLE, true);
ACLUtil.setAddMetadataPermission(graph, Constants.NOONE_ROLE, true);
ACLUtil.setRemoveMetadataPermission(graph, Constants.NOONE_ROLE, true);
} else {
ACLUtil.setReadPermission(graph, Constants.EVERYONE_ROLE, true);
}
JavaSemanticService javaService = SemanticServiceFactory.createJavaSemanticService(serviceUri, serviceRegistry);
javaService.setServiceUser(service.getServiceUser());
if (service.isLongRunning()) {
javaService.setStateStyle(StateStyleEnum.LongRunningStyle);
} else {
javaService.setStateStyle(StateStyleEnum.ConnectionStyle);
}
Method[] methods = service.getClass().getMethods();
ArrayList<String> operations = new ArrayList<String>();
DynamicServiceStats serviceStats = service.getStatistics();
for (int i = 0; i < methods.length; i++) {
@SuppressWarnings("unchecked")
Class[] params = methods[i].getParameterTypes();
if (params.length != 3) {
continue;
} else {
if (params[0].equals(IExecutionContext.class) && params[1].equals(IDataset.class) && params[2].equals(IDataset.class)) {
operations.add(methods[i].getName());
if (serviceStats != null) {
serviceStats.addMethod(methods[i].getName());
}
}
}
}
if (serviceStats != null) {
operations.add(ENABLE_STATS);
operations.add(DISABLE_STATS);
}
for (String op : operations) {
URI opUri = Constants.valueFactory.createURI(serviceUri.toString() + "#" + op);
SemanticOperation operation = SemanticServiceFactory.createSemanticOperation(opUri, javaService.graph());
javaService.addOperation(operation);
}
}
}
void unregisterService(IBundledSemanticService service) {
try {
stopService(service.getServiceUri(), true);
} catch (AnzoException e) {
log.error("Could not stop service: " + service.getServiceUri(), e);
}
services.remove(service.getServiceUri());
}
Collection<JavaSemanticService> registerServiceFactory(String serviceFactoryPid, ISemanticServiceFactory serviceFactory) throws AnzoException {
serviceFactories.put(serviceFactoryPid, serviceFactory);
// TODO: query the registry to see if we have a service that uses this factory that
// couldn't start because the factory wasn't registered yet.
Collection<Statement> stmts = serviceRegistry.find(null, JavaSemanticService.serviceFactoryPidProperty, Constants.valueFactory.createTypedLiteral(serviceFactoryPid));
for (Statement stmt : stmts) {
URI serviceUri = (URI) stmt.getSubject();
if (!services.containsKey(serviceUri)) {
JavaSemanticService service = SemanticServiceFactory.getJavaSemanticService(serviceUri, serviceRegistry);
ISemanticService javaService = serviceFactory.createService(service);
// the service will get initialized by the loadService call, which will call initializeService
services.put(serviceUri, javaService);
loadService(service);
factoryServices.put(serviceFactoryPid, service);
}
}
return factoryServices.getCollection(serviceFactoryPid);
}
Collection<JavaSemanticService> unregisterServiceFactory(String serviceFactoryPid) {
// should we be maintaining a list of services created by the factory and stop them here?
serviceFactories.remove(serviceFactoryPid);
return factoryServices.remove(serviceFactoryPid);
}
@Override
public boolean initializeService(SemanticService service, AnzoClient anzoClient) throws AnzoException {
if (services.containsKey(service.resource())) {
ISemanticService ss = services.get(service.resource());
JavaSemanticService javaService = SemanticServiceFactory.getJavaSemanticService(service.resource(), service.graph());
ss.initialize(javaService, anzoClient);
return true;
} else {
// otherwise check if a factory has been registered for it.
JavaSemanticService javaService = SemanticServiceFactory.getJavaSemanticService(service.resource(), service.graph().getNamedGraphUri(), service.dataset());
String factoryPid = javaService.getServiceFactoryPid();
ISemanticServiceFactory factory = serviceFactories.get(factoryPid);
if (factory == null) {
return false;
}
ISemanticService ss = factory.createService(javaService);
ss.initialize(javaService, anzoClient);
services.put((URI) service.resource(), ss);
return true;
}
}
@Override
public void stopService(URI serviceUri, AnzoClient anzoClient) throws AnzoException {
ISemanticService ss = services.get(serviceUri);
if (ss != null) {
ss.stop(anzoClient);
// remove anything created by a factory since when we load the service again,
// we'll recreate the instance.
if (!(ss instanceof IBundledSemanticService)) {
services.remove(serviceUri);
}
}
}
@Override
public void verifyOperation(URI serviceUri, URI operationUri) throws AnzoException {
try {
ISemanticService service = services.get(serviceUri);
String operationName = operationUri.getLocalName();
if (ENABLE_STATS.equals(operationName)) {
if (service.getStatistics() == null)
throw new Exception("No statistics object to enable");
} else if (DISABLE_STATS.equals(operationName)) {
if (service.getStatistics() == null)
throw new Exception("No statistics object to disable");
} else {
service.getClass().getMethod(operationUri.getLocalName(), IExecutionContext.class, IDataset.class, IDataset.class);
}
} catch (Exception e) {
throw new AnzoException(ExceptionConstants.EXECUTION.SERVICE_INITIALIZATION_ERROR, e);
}
}
@Override
public void executeService(URI serviceUri, URI operationUri, IExecutionContext ec, IDataset request, IDataset response) throws AnzoException {
try {
ISemanticService ss = services.get(serviceUri);
long start = 0;
if (ss.getStatistics() != null && ss.getStatistics().isEnabled()) {
start = System.currentTimeMillis();
}
String operation = operationUri.getLocalName();
if (ENABLE_STATS.equals(operation)) {
ss.getStatistics().setEnabled(true);
} else if (DISABLE_STATS.equals(operation)) {
ss.getStatistics().setEnabled(false);
} else {
try {
Method method = ss.getClass().getMethod(operation, IExecutionContext.class, IDataset.class, IDataset.class);
method.invoke(ss, ec, request, response);
} finally {
if (ss.getStatistics() != null && ss.getStatistics().isEnabled()) {
ss.getStatistics().use(operation, System.currentTimeMillis() - start);
}
}
}
} catch (NoSuchMethodException e) {
throw new AnzoException(ExceptionConstants.EXECUTION.UNKNOWN_OPERATION_ERROR, e, serviceUri.toString(), operationUri.toString());
} catch (IllegalAccessException e) {
throw new AnzoException(ExceptionConstants.EXECUTION.EXECUTION_ERROR, e, serviceUri.toString(), operationUri.toString());
} catch (InvocationTargetException e) {
throw new AnzoException(ExceptionConstants.EXECUTION.EXECUTION_ERROR, e.getTargetException(), serviceUri.toString(), operationUri.toString(), e.getTargetException().getMessage());
}
}
public URI getServiceTypeUri() {
return JavaSemanticService.TYPE;
}
public URI getServiceUri(URI operationUri, IDataset request) throws AnzoException {
return null;
}
@Override
public void registryReset(RegistryDataset registry) throws AnzoException {
for (Map.Entry<URI, ISemanticService> entry : services.entrySet()) {
if (entry.getValue() instanceof IBundledSemanticService) {
registerService((IBundledSemanticService) entry.getValue(), registry);
}
}
for (Map.Entry<String, ISemanticServiceFactory> entry : serviceFactories.entrySet()) {
registerServiceFactory(entry.getKey(), entry.getValue());
}
}
}