/******************************************************************************* * Copyright 2011 * Ubiquitous Knowledge Processing (UKP) Lab * Technische Universität Darmstadt * * Licensed 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.dkpro.lab.uima.engine.uimaas; import static org.dkpro.lab.Util.close; import static org.dkpro.lab.Util.getUrlAsFile; import static org.apache.uima.UIMAFramework.newDefaultResourceManager; import static org.apache.uima.fit.factory.ExternalResourceFactory.bindResource; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.uima.UIMAFramework; import org.apache.uima.aae.client.UimaAsBaseCallbackListener; import org.apache.uima.aae.client.UimaAsynchronousEngine; import org.apache.uima.adapter.jms.client.BaseUIMAAsynchronousEngine_impl; import org.apache.uima.analysis_engine.AnalysisEngineDescription; import org.apache.uima.cas.CAS; import org.apache.uima.collection.EntityProcessStatus; import org.apache.uima.resource.ResourceInitializationException; import org.apache.uima.resource.ResourceManager; import org.apache.uima.resource.ResourceProcessException; import org.apache.uima.resource.metadata.ResourceMetaData; import org.apache.uima.resourceSpecifier.factory.DeploymentDescriptorFactory; import org.apache.uima.resourceSpecifier.factory.ServiceContext; import org.apache.uima.resourceSpecifier.factory.UimaASPrimitiveDeploymentDescriptor; import org.apache.uima.resourceSpecifier.factory.impl.ServiceContextImpl; import org.dkpro.lab.engine.ExecutionException; import org.dkpro.lab.engine.LifeCycleException; import org.dkpro.lab.engine.TaskContext; import org.dkpro.lab.engine.TaskContextFactory; import org.dkpro.lab.engine.TaskExecutionEngine; import org.dkpro.lab.task.Task; import org.dkpro.lab.uima.task.TaskContextProvider; import org.dkpro.lab.uima.task.UimaTask; import org.xml.sax.SAXException; /** * UIMA AS-based execution engine. An {@link UimaTask} is be executed using the UIMA AS framework. * This is currently a simple proof-of-concept implementation. */ public class UimaAsExecutionEngine implements TaskExecutionEngine { private final Log log = LogFactory.getLog(getClass()); private TaskContextFactory contextFactory; private String brokerUrl = "tcp://localhost:61616"; private String endpoint = "experiment"; private int casPoolSize = 2; private int fsHeapSize = 2000000; private int timeout = 0; private int getmeta_timeout = 60; private int cpc_timeout = 0; private TaskContext ctx; private UimaAsynchronousEngine uimaAsEngine; private String serviceId; private UimaTask configuration; @Override public String run(Task aConfiguration) throws ExecutionException, LifeCycleException { if (!(aConfiguration instanceof UimaTask)) { throw new ExecutionException("This engine can only execute [" + UimaTask.class.getName() + "]"); } configuration = (UimaTask) aConfiguration; ctx = contextFactory.createContext(aConfiguration); try { ResourceManager resMgr = newDefaultResourceManager(); // Make sure the descriptor is fully resolved. It will be modified and // thus should not be modified again afterwards by UIMA. AnalysisEngineDescription analysisDesc = configuration .getAnalysisEngineDescription(ctx); analysisDesc.resolveImports(resMgr); // Scan components that accept the service and bind it to them bindResource(analysisDesc, TaskContext.class, TaskContextProvider.class, TaskContextProvider.PARAM_FACTORY_NAME, contextFactory.getId(), TaskContextProvider.PARAM_CONTEXT_ID, ctx.getId()); ctx.message("Bound external resources"); // Now the setup is complete ctx.getLifeCycleManager().initialize(ctx, aConfiguration); // Deploy experiment as UIMA-AS service initializeService(); // Initialize the client initializeClient(); // Start recording ctx.getLifeCycleManager().begin(ctx, aConfiguration); // Run the experiment process(); // End recording ctx.getLifeCycleManager().complete(ctx, aConfiguration); return ctx.getId(); } catch (LifeCycleException e) { ctx.getLifeCycleManager().fail(ctx, aConfiguration, e); throw e; } catch (Exception e) { ctx.getLifeCycleManager().fail(ctx, aConfiguration, e); throw new ExecutionException(e); } finally { destroy(); } } protected void initializeService() throws Exception { // Create Asynchronous Engine API uimaAsEngine = new BaseUIMAAsynchronousEngine_impl(); // Save the AED to a file because UIMA-AS cannot have an AED direclty embedded in its // descriptor AnalysisEngineDescription topDescriptor = configuration.getAnalysisEngineDescription(ctx); ResourceMetaData topMetaData = topDescriptor.getMetaData(); File topDescriptorFile = File.createTempFile(getClass() .getSimpleName(), ".xml"); topDescriptorFile.deleteOnExit(); try (OutputStream os = new FileOutputStream(topDescriptorFile)) { topDescriptor.toXML(os); } // Create service descriptor ServiceContext context = new ServiceContextImpl(topMetaData.getName(), topMetaData.getDescription(), topDescriptorFile.getAbsolutePath(), endpoint, brokerUrl); UimaASPrimitiveDeploymentDescriptor dd = DeploymentDescriptorFactory .createPrimitiveDeploymentDescriptor(context); // Store service descriptor also to a temporary file File deploymentDescriptionFile = File.createTempFile(getClass().getSimpleName(), ".xml"); deploymentDescriptionFile.deleteOnExit(); dd.save(deploymentDescriptionFile); Map<String, Object> serviceCtx = new HashMap<String, Object>(); serviceCtx.put(UimaAsynchronousEngine.DD2SpringXsltFilePath, getUrlAsFile( getClass().getResource("/uima-as/dd2spring.xsl"), true).getAbsolutePath()); serviceCtx.put(UimaAsynchronousEngine.SaxonClasspath, getClass().getResource( "/uima-as/saxon8.jar").toString()); serviceId = uimaAsEngine.deploy(deploymentDescriptionFile.getAbsolutePath(), serviceCtx); ctx.message("Deployed experiment as UIMA-AS service: [" + serviceId + "]"); } /** * Initialize the UIMA-AS client. */ protected void initializeClient() throws ResourceInitializationException, IOException { Map<String, Object> clientCtx = new HashMap<String, Object>(); clientCtx.put(UimaAsynchronousEngine.ServerUri, brokerUrl); clientCtx.put(UimaAsynchronousEngine.ENDPOINT, endpoint); clientCtx.put(UimaAsynchronousEngine.Timeout, timeout * 1000); clientCtx.put(UimaAsynchronousEngine.GetMetaTimeout, getmeta_timeout * 1000); clientCtx.put(UimaAsynchronousEngine.CpcTimeout, cpc_timeout * 1000); clientCtx.put(UimaAsynchronousEngine.CasPoolSize, casPoolSize); clientCtx.put(UIMAFramework.CAS_INITIAL_HEAP_SIZE, new Integer(fsHeapSize / 4).toString()); // Add Collection Reader uimaAsEngine.setCollectionReader(UIMAFramework.produceCollectionReader(configuration .getCollectionReaderDescription(ctx))); // Add status listener uimaAsEngine.addStatusCallbackListener(new StatusCallbackListenerImpl(ctx)); // Initialize the client uimaAsEngine.initialize(clientCtx); ctx.message("Initialized asynchronous client"); } protected void process() throws ResourceProcessException { uimaAsEngine.process(); } /** * Shut the UIMA-AS client down. */ protected void shutdownClient() { if (uimaAsEngine != null) { try { uimaAsEngine.stop(); ctx.message("Shut down asynchronous client"); } catch (Exception e) { log.error("Error shutting down asynchronous client", e); } uimaAsEngine = null; } } /** * Un-deploy the experiment service. */ protected void shutdownService() { if (serviceId != null) { try { // Undeploy experiment uimaAsEngine.undeploy(serviceId); ctx.message("Undeployed experiment service ["+serviceId+"]"); } catch (Exception e) { log.error("Error undeploying experiment service", e); } serviceId = null; } } /** * Release all resources. */ protected void destroy() { shutdownService(); shutdownClient(); if (ctx != null) { ctx.destroy(); } } @Override public void setContextFactory(TaskContextFactory aContextFactory) { contextFactory = aContextFactory; } /** * Callback Listener. Receives event notifications from UIMA AS. */ static class StatusCallbackListenerImpl extends UimaAsBaseCallbackListener { private final TaskContext ctx; int entityCount = 0; public StatusCallbackListenerImpl(final TaskContext aCtx) { ctx = aCtx; } /** * Called when the initialization is completed. */ @Override public void initializationComplete(EntityProcessStatus aStatus) { if (aStatus != null && aStatus.isException()) { ctx.message("Error on getMeta call to remote service:"); List<Exception> exceptions = aStatus.getExceptions(); for (int i = 0; i < exceptions.size(); i++) { ((Throwable) exceptions.get(i)).printStackTrace(); } } ctx.message("UIMAEE Initialization Complete"); } /** * Called when the collection processing is completed. */ @Override public void collectionProcessComplete(EntityProcessStatus aStatus) { if (aStatus != null && aStatus.isException()) { ctx.message("Error on collection process complete call to remote service:"); List<Exception> exceptions = aStatus.getExceptions(); for (int i = 0; i < exceptions.size(); i++) { ((Throwable) exceptions.get(i)).printStackTrace(); } } ctx.message("Completed " + entityCount + " documents"); } /** * Called when the processing of a Document is completed. <br> * The process status can be looked at and corresponding actions taken. * * @param aCas * CAS corresponding to the completed processing * @param aStatus * EntityProcessStatus that holds the status of all the events for aEntity */ @Override public void entityProcessComplete(CAS aCas, EntityProcessStatus aStatus) { if (aStatus != null && aStatus.isException()) { System.err.println("Error on process CAS call to remote service:"); List<Exception> exceptions = aStatus.getExceptions(); for (int i = 0; i < exceptions.size(); i++) { ((Throwable) exceptions.get(i)).printStackTrace(); } } } } }