You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by re...@apache.org on 2022/04/08 13:41:57 UTC
[uima-uimaj] 01/01: [UIMA-6431] Use lambda functions as CAS processors
This is an automated email from the ASF dual-hosted git repository.
rec pushed a commit to branch refactoring/UIMA-6431-Use-lambda-functions-as-CAS-processors
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git
commit 16ce603dc965be9475c0b415d63e4cc5f634ecaf
Author: Richard Eckart de Castilho <re...@apache.org>
AuthorDate: Fri Apr 8 15:41:50 2022 +0200
[UIMA-6431] Use lambda functions as CAS processors
- Add default implementatins to AnalysisEngineServiceStub and ResourceServiceStub to reduce verbosity
- Add analysis engine implementations using CAS- or JCas-accepting methods
- Added tests
---
.../uima/analysis_component/CasProcessor.java | 60 +++++++++++++++
.../analysis_component/CasProcessorAnnotator.java | 77 +++++++++++++++++++
.../analysis_component/JCasAnnotator_ImplBase.java | 1 -
.../uima/analysis_component/JCasProcessor.java | 60 +++++++++++++++
.../analysis_component/JCasProcessorAnnotator.java | 87 ++++++++++++++++++++++
.../analysis_engine/AnalysisEngineServiceStub.java | 8 +-
.../AnalysisEngineProcessorAdapter.java} | 70 +++++------------
.../impl/AnalysisEngineProcessorStub.java} | 41 +++++++---
.../service/impl/AnalysisEngineServiceAdapter.java | 5 +-
.../apache/uima/resource/ResourceServiceStub.java | 7 +-
.../CasProcessorAnnotatorTest.java | 82 ++++++++++++++++++++
.../JCasProcessorAnnotatorTest.java | 82 ++++++++++++++++++++
12 files changed, 509 insertions(+), 71 deletions(-)
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_component/CasProcessor.java b/uimaj-core/src/main/java/org/apache/uima/analysis_component/CasProcessor.java
new file mode 100644
index 000000000..96b4c7e65
--- /dev/null
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_component/CasProcessor.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.uima.analysis_component;
+
+import java.util.Objects;
+
+import org.apache.uima.cas.CAS;
+
+/**
+ * A functional interface for a CAS processor.
+ *
+ * @param <E>
+ * Thrown exception.
+ */
+@FunctionalInterface
+public interface CasProcessor<E extends Throwable> {
+
+ /**
+ * Accepts the processor.
+ *
+ * @param aCas
+ * the CAS to process.
+ * @throws E
+ * Thrown when the processor fails.
+ */
+ void process(CAS aCas) throws E;
+
+ /**
+ * Returns a composed {@code CasProcessor} like {@link CasProcessor#andThen(CasProcessor)}.
+ *
+ * @param after
+ * the operation to perform after this operation
+ * @return a composed {@code CasProcessor} like {@link CasProcessor#andThen(CasProcessor)}.
+ * @throws NullPointerException
+ * when {@code after} is null
+ */
+ default CasProcessor<E> andThen(final CasProcessor<E> after) {
+ Objects.requireNonNull(after);
+ return (final CAS t) -> {
+ process(t);
+ after.process(t);
+ };
+ }
+}
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_component/CasProcessorAnnotator.java b/uimaj-core/src/main/java/org/apache/uima/analysis_component/CasProcessorAnnotator.java
new file mode 100644
index 000000000..eeef72abb
--- /dev/null
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_component/CasProcessorAnnotator.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.uima.analysis_component;
+
+import java.util.Map;
+
+import org.apache.uima.UIMAFramework;
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.analysis_engine.impl.AnalysisEngineProcessorAdapter;
+import org.apache.uima.analysis_engine.impl.AnalysisEngineProcessorStub;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.resource.ResourceInitializationException;
+import org.apache.uima.resource.ResourceSpecifier;
+import org.apache.uima.resource.metadata.ResourceMetaData;
+
+public class CasProcessorAnnotator extends AnalysisEngineProcessorAdapter {
+ private ResourceMetaData metaData;
+ private CasProcessor<? extends Exception> delegate;
+
+ public CasProcessorAnnotator(CasProcessor<? extends Exception> aCasAnnotator) {
+ metaData = UIMAFramework.getResourceSpecifierFactory().createAnalysisEngineMetaData();
+ delegate = aCasAnnotator;
+ }
+
+ @Override
+ public boolean initialize(ResourceSpecifier aSpecifier, Map<String, Object> aAdditionalParams)
+ throws ResourceInitializationException {
+ setStub(makeDelegate());
+ return super.initialize(aSpecifier, aAdditionalParams);
+ }
+
+ private AnalysisEngineProcessorStub makeDelegate() {
+ return new AnalysisEngineProcessorStub() {
+
+ @Override
+ public ResourceMetaData getMetaData() {
+ return metaData;
+ }
+
+ @Override
+ public void process(CAS aCAS) throws AnalysisEngineProcessException {
+ try {
+ delegate.process(aCAS);
+ } catch (Exception e) {
+ if (e instanceof AnalysisEngineProcessException) {
+ throw (AnalysisEngineProcessException) e;
+ } else {
+ throw new AnalysisEngineProcessException(e);
+ }
+ }
+ }
+ };
+ }
+
+ public static CasProcessorAnnotator of(CasProcessor<? extends Exception> aCasAnnotator)
+ throws ResourceInitializationException {
+ CasProcessorAnnotator engine = new CasProcessorAnnotator(aCasAnnotator);
+ engine.initialize(null, null);
+ return engine;
+ }
+}
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasAnnotator_ImplBase.java b/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasAnnotator_ImplBase.java
index 598edf3c5..9c450d166 100644
--- a/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasAnnotator_ImplBase.java
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasAnnotator_ImplBase.java
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package org.apache.uima.analysis_component;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasProcessor.java b/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasProcessor.java
new file mode 100644
index 000000000..4bcc3160d
--- /dev/null
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasProcessor.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.uima.analysis_component;
+
+import java.util.Objects;
+
+import org.apache.uima.jcas.JCas;
+
+/**
+ * A functional interface for a JCas processor.
+ *
+ * @param <E>
+ * Thrown exception.
+ */
+@FunctionalInterface
+public interface JCasProcessor<E extends Throwable> {
+
+ /**
+ * Accepts the processor.
+ *
+ * @param aJCas
+ * the JCas to process.
+ * @throws E
+ * Thrown when the processor fails.
+ */
+ void process(JCas aJCas) throws E;
+
+ /**
+ * Returns a composed {@code JCasProcessor} like {@link JCasProcessor#andThen(JCasProcessor)}.
+ *
+ * @param after
+ * the operation to perform after this operation
+ * @return a composed {@code JCasProcessor} like {@link JCasProcessor#andThen(JCasProcessor)}.
+ * @throws NullPointerException
+ * when {@code after} is null
+ */
+ default JCasProcessor<E> andThen(final JCasProcessor<E> after) {
+ Objects.requireNonNull(after);
+ return (final JCas t) -> {
+ process(t);
+ after.process(t);
+ };
+ }
+}
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasProcessorAnnotator.java b/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasProcessorAnnotator.java
new file mode 100644
index 000000000..8cecdc506
--- /dev/null
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_component/JCasProcessorAnnotator.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.uima.analysis_component;
+
+import java.util.Map;
+
+import org.apache.uima.UIMAFramework;
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.analysis_engine.impl.AnalysisEngineProcessorAdapter;
+import org.apache.uima.analysis_engine.impl.AnalysisEngineProcessorStub;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.CASException;
+import org.apache.uima.jcas.JCas;
+import org.apache.uima.resource.ResourceInitializationException;
+import org.apache.uima.resource.ResourceSpecifier;
+import org.apache.uima.resource.metadata.ResourceMetaData;
+
+public class JCasProcessorAnnotator extends AnalysisEngineProcessorAdapter {
+ private ResourceMetaData metaData;
+ private JCasProcessor<? extends Exception> delegate;
+
+ public JCasProcessorAnnotator(
+ JCasProcessor<? extends Exception> aJCasAnnotator) {
+ metaData = UIMAFramework.getResourceSpecifierFactory().createAnalysisEngineMetaData();
+ delegate = aJCasAnnotator;
+ }
+
+ @Override
+ public boolean initialize(ResourceSpecifier aSpecifier, Map<String, Object> aAdditionalParams)
+ throws ResourceInitializationException {
+ setStub(makeDelegate());
+ return super.initialize(aSpecifier, aAdditionalParams);
+ }
+
+ private AnalysisEngineProcessorStub makeDelegate() {
+ return new AnalysisEngineProcessorStub() {
+
+ @Override
+ public ResourceMetaData getMetaData() {
+ return metaData;
+ }
+
+ @Override
+ public void process(CAS aCAS) throws AnalysisEngineProcessException {
+ JCas jcas;
+ try {
+ jcas = aCAS.getJCas();
+ } catch (CASException e) {
+ throw new AnalysisEngineProcessException(e);
+ }
+ try {
+ delegate.process(jcas);
+ } catch (Exception e) {
+ if (e instanceof AnalysisEngineProcessException) {
+ throw (AnalysisEngineProcessException) e;
+ } else {
+ throw new AnalysisEngineProcessException(e);
+ }
+ }
+ }
+ };
+ }
+
+ public static JCasProcessorAnnotator of(
+ JCasProcessor<? extends AnalysisEngineProcessException> aJCasAnnotator)
+ throws ResourceInitializationException {
+ JCasProcessorAnnotator engine = new JCasProcessorAnnotator(aJCasAnnotator);
+ engine.initialize(null, null);
+ return engine;
+ }
+}
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineServiceStub.java b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineServiceStub.java
index c83cfacb6..4f287d8df 100644
--- a/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineServiceStub.java
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineServiceStub.java
@@ -43,7 +43,9 @@ public interface AnalysisEngineServiceStub extends ResourceServiceStub {
* @throws ResourceServiceException
* tbd
*/
- void callBatchProcessComplete() throws ResourceServiceException;
+ default void callBatchProcessComplete() throws ResourceServiceException {
+ // No action by default.
+ }
/**
* Performs service call to inform the AnalysisEngine that the processing of a collection has been
@@ -52,5 +54,7 @@ public interface AnalysisEngineServiceStub extends ResourceServiceStub {
* @throws ResourceServiceException
* tbd
*/
- void callCollectionProcessComplete() throws ResourceServiceException;
+ default void callCollectionProcessComplete() throws ResourceServiceException {
+ // No action by default.
+ }
}
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_engine/service/impl/AnalysisEngineServiceAdapter.java b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineProcessorAdapter.java
similarity index 69%
copy from uimaj-core/src/main/java/org/apache/uima/analysis_engine/service/impl/AnalysisEngineServiceAdapter.java
copy to uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineProcessorAdapter.java
index 197f57cef..c99ac1fa2 100644
--- a/uimaj-core/src/main/java/org/apache/uima/analysis_engine/service/impl/AnalysisEngineServiceAdapter.java
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineProcessorAdapter.java
@@ -17,53 +17,35 @@
* under the License.
*/
-package org.apache.uima.analysis_engine.service.impl;
-
-import java.util.Map;
+package org.apache.uima.analysis_engine.impl;
import org.apache.uima.UIMAFramework;
-import org.apache.uima.UIMARuntimeException;
import org.apache.uima.UIMA_UnsupportedOperationException;
-import org.apache.uima.analysis_engine.AnalysisEngine;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
-import org.apache.uima.analysis_engine.AnalysisEngineServiceStub;
import org.apache.uima.analysis_engine.CasIterator;
import org.apache.uima.analysis_engine.TextAnalysisEngine;
-import org.apache.uima.analysis_engine.impl.AnalysisEngineImplBase;
-import org.apache.uima.analysis_engine.impl.EmptyCasIterator;
import org.apache.uima.cas.CAS;
import org.apache.uima.collection.CasConsumer;
import org.apache.uima.resource.ResourceConfigurationException;
-import org.apache.uima.resource.ResourceServiceException;
-import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.metadata.ResourceMetaData;
import org.apache.uima.util.Level;
import org.apache.uima.util.UimaTimer;
/**
- * Base class for analysis engine service adapters. Implements the {@link AnalysisEngine} interface
- * by communicating with an Analysis Engine service. This insulates the application from having to
- * know whether it is calling a local AnalysisEngine or a remote service.
- * <p>
- * Subclasses must provide an implementation of the {@link #initialize(ResourceSpecifier,Map)}
- * method, which must create an {@link AnalysisEngineServiceStub} object that can communicate with
- * the remote service. The stub must be passed to the {@link #setStub(AnalysisEngineServiceStub)}
- * method of this class.
- *
- *
+ * Base class for analysis engine processor adapters. Used for embedded (functional) processors.
*/
-public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBase
+public abstract class AnalysisEngineProcessorAdapter extends AnalysisEngineImplBase
implements TextAnalysisEngine, CasConsumer {
/**
* current class
*/
- private static final Class<AnalysisEngineServiceAdapter> CLASS_NAME = AnalysisEngineServiceAdapter.class;
+ private static final Class<AnalysisEngineProcessorAdapter> CLASS_NAME = AnalysisEngineProcessorAdapter.class;
/**
- * The stub that communicates with the remote service.
+ * The stub that talks to the actual implementation.
*/
- private AnalysisEngineServiceStub mStub;
+ private AnalysisEngineProcessorStub mStub;
/**
* The resource metadata, cached so that service does not have to be called each time metadata is
@@ -77,13 +59,13 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
private UimaTimer mTimer = UIMAFramework.newTimer();
/**
- * Sets the stub to be used to communicate with the remote service. Subclasses must call this from
- * their <code>initialize</code> method.
+ * Sets the stub to be used to actual implementation. Subclasses must call this from their
+ * <code>initialize</code> method.
*
* @param aStub
* the stub for the remote service
*/
- protected void setStub(AnalysisEngineServiceStub aStub) {
+ protected void setStub(AnalysisEngineProcessorStub aStub) {
mStub = aStub;
}
@@ -92,7 +74,7 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
*
* @return the stub for the remote service
*/
- protected AnalysisEngineServiceStub getStub() {
+ protected AnalysisEngineProcessorStub getStub() {
return mStub;
}
@@ -101,14 +83,8 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
*/
@Override
public ResourceMetaData getMetaData() {
- try {
- if (mCachedMetaData == null && getStub() != null) {
- mCachedMetaData = getStub().callGetMetaData();
- }
- return mCachedMetaData;
- } catch (ResourceServiceException e) {
- throw new UIMARuntimeException(e);
- }
+
+ return getStub() != null ? getStub().getMetaData() : null;
}
/**
@@ -116,8 +92,9 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
*/
@Override
public void destroy() {
- if (getStub() != null)
+ if (getStub() != null) {
getStub().destroy();
+ }
super.destroy();
}
@@ -129,7 +106,7 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
LOG_RESOURCE_BUNDLE, "UIMA_analysis_engine_process_begin__FINE", getResourceName());
try {
// invoke service
- getStub().callProcess(aCAS);
+ getStub().process(aCAS);
// log end of event
UIMAFramework.getLogger(CLASS_NAME).logrb(Level.FINE, CLASS_NAME.getName(), "process",
@@ -138,10 +115,9 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
// we don't support CasMultiplier services yet, so this always returns
// an empty iterator
return new EmptyCasIterator();
+ } catch (AnalysisEngineProcessException e) {
+ throw e;
} catch (Exception e) {
- // log exception
- UIMAFramework.getLogger(CLASS_NAME).log(Level.SEVERE, "", e);
- // rethrow as AnalysisEngineProcessException
throw new AnalysisEngineProcessException(e);
} finally {
mTimer.stopIt();
@@ -199,20 +175,12 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
@Override
public void batchProcessComplete() throws AnalysisEngineProcessException {
- try {
- getStub().callBatchProcessComplete();
- } catch (ResourceServiceException e) {
- throw new AnalysisEngineProcessException(e);
- }
+ getStub().batchProcessComplete();
}
@Override
public void collectionProcessComplete() throws AnalysisEngineProcessException {
- try {
- getStub().callCollectionProcessComplete();
- } catch (ResourceServiceException e) {
- throw new AnalysisEngineProcessException(e);
- }
+ getStub().collectionProcessComplete();
}
/**
diff --git a/uimaj-core/src/main/java/org/apache/uima/resource/ResourceServiceStub.java b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineProcessorStub.java
similarity index 52%
copy from uimaj-core/src/main/java/org/apache/uima/resource/ResourceServiceStub.java
copy to uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineProcessorStub.java
index 3b7028437..28e390a97 100644
--- a/uimaj-core/src/main/java/org/apache/uima/resource/ResourceServiceStub.java
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/impl/AnalysisEngineProcessorStub.java
@@ -16,28 +16,49 @@
* specific language governing permissions and limitations
* under the License.
*/
+package org.apache.uima.analysis_engine.impl;
-package org.apache.uima.resource;
-
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.cas.CAS;
import org.apache.uima.resource.metadata.ResourceMetaData;
/**
* A stub that calls a remote AnalysisEngine service.
- *
- *
*/
-public interface ResourceServiceStub {
+public interface AnalysisEngineProcessorStub {
/**
* Performs service call to retrieve resource meta data.
*
* @return metadata for the Resource
- * @throws ResourceServiceException
- * passthru
*/
- ResourceMetaData callGetMetaData() throws ResourceServiceException;
+ ResourceMetaData getMetaData();
+
+ /**
+ * Performs service call to process an entity.
+ *
+ * @param aCAS
+ * the CAS to process
+ */
+ void process(CAS aCAS) throws AnalysisEngineProcessException;
+
+ /**
+ * Notify the stub that all items in the batch have been processed.
+ */
+ default void batchProcessComplete() throws AnalysisEngineProcessException {
+ // No action by default.
+ }
+
+ /**
+ * Notify the stub that all items in the collection have been processed.
+ */
+ default void collectionProcessComplete() throws AnalysisEngineProcessException {
+ // No action by default.
+ }
/**
- * Called when this stub is no longer needed, so any open connections can be closed.
+ * Called when this stub is no longer needed, so resources can be cleaned up.
*/
- void destroy();
+ default void destroy() {
+ // No action by default
+ }
}
diff --git a/uimaj-core/src/main/java/org/apache/uima/analysis_engine/service/impl/AnalysisEngineServiceAdapter.java b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/service/impl/AnalysisEngineServiceAdapter.java
index 197f57cef..3c440ed98 100644
--- a/uimaj-core/src/main/java/org/apache/uima/analysis_engine/service/impl/AnalysisEngineServiceAdapter.java
+++ b/uimaj-core/src/main/java/org/apache/uima/analysis_engine/service/impl/AnalysisEngineServiceAdapter.java
@@ -49,8 +49,6 @@ import org.apache.uima.util.UimaTimer;
* method, which must create an {@link AnalysisEngineServiceStub} object that can communicate with
* the remote service. The stub must be passed to the {@link #setStub(AnalysisEngineServiceStub)}
* method of this class.
- *
- *
*/
public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBase
implements TextAnalysisEngine, CasConsumer {
@@ -116,8 +114,9 @@ public abstract class AnalysisEngineServiceAdapter extends AnalysisEngineImplBas
*/
@Override
public void destroy() {
- if (getStub() != null)
+ if (getStub() != null) {
getStub().destroy();
+ }
super.destroy();
}
diff --git a/uimaj-core/src/main/java/org/apache/uima/resource/ResourceServiceStub.java b/uimaj-core/src/main/java/org/apache/uima/resource/ResourceServiceStub.java
index 3b7028437..e81ba14ca 100644
--- a/uimaj-core/src/main/java/org/apache/uima/resource/ResourceServiceStub.java
+++ b/uimaj-core/src/main/java/org/apache/uima/resource/ResourceServiceStub.java
@@ -16,15 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package org.apache.uima.resource;
import org.apache.uima.resource.metadata.ResourceMetaData;
/**
* A stub that calls a remote AnalysisEngine service.
- *
- *
*/
public interface ResourceServiceStub {
/**
@@ -39,5 +36,7 @@ public interface ResourceServiceStub {
/**
* Called when this stub is no longer needed, so any open connections can be closed.
*/
- void destroy();
+ default void destroy() {
+ // No action by default
+ }
}
diff --git a/uimaj-core/src/test/java/org/apache/uima/analysis_component/CasProcessorAnnotatorTest.java b/uimaj-core/src/test/java/org/apache/uima/analysis_component/CasProcessorAnnotatorTest.java
new file mode 100644
index 000000000..3f66c514b
--- /dev/null
+++ b/uimaj-core/src/test/java/org/apache/uima/analysis_component/CasProcessorAnnotatorTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.uima.analysis_component;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import org.apache.uima.analysis_engine.AnalysisEngine;
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.util.CasCreationUtils;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class CasProcessorAnnotatorTest {
+
+ private static final String NEEDLE = "needle";
+
+ private CAS cas;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ cas = CasCreationUtils.createCas();
+ }
+
+ @Test
+ void thatProcessingWithJCasInterfaceWorks() throws Exception {
+ AnalysisEngine engine = CasProcessorAnnotator.of(_cas -> _cas.setDocumentText(NEEDLE));
+ engine.process(cas.getJCas());
+
+ assertThat(cas.getDocumentText()).isEqualTo(NEEDLE);
+ }
+
+ @Test
+ void thatProcessingWithCasInterfaceWorks() throws Exception {
+ AnalysisEngine engine = CasProcessorAnnotator.of(_cas -> _cas.setDocumentText(NEEDLE));
+ engine.process(cas);
+
+ assertThat(cas.getDocumentText()).isEqualTo(NEEDLE);
+ }
+
+ @Test
+ void thatAnalysisEngineProcessExceptionMakesItThrough() throws Exception {
+ AnalysisEngine engine = CasProcessorAnnotator.of(_cas -> {
+ throw new AnalysisEngineProcessException(NEEDLE, null);
+ });
+
+ assertThatExceptionOfType(AnalysisEngineProcessException.class)
+ .isThrownBy(() -> engine.process(cas)) //
+ .matches(ex -> NEEDLE.equals(ex.getMessageKey()));
+ }
+
+ @Test
+ void thatGenericExceptionIsWrapped() throws Exception {
+ AnalysisEngine engine = CasProcessorAnnotator.of(_cas -> {
+ throw new RuntimeException(NEEDLE);
+ });
+
+ Assertions.setMaxStackTraceElementsDisplayed(1000);
+ assertThatExceptionOfType(AnalysisEngineProcessException.class)
+ .isThrownBy(() -> engine.process(cas)) //
+ .matches(ex -> ex.getCause() instanceof RuntimeException)
+ .matches(ex -> NEEDLE.equals(ex.getCause().getMessage()));
+ }
+}
diff --git a/uimaj-core/src/test/java/org/apache/uima/analysis_component/JCasProcessorAnnotatorTest.java b/uimaj-core/src/test/java/org/apache/uima/analysis_component/JCasProcessorAnnotatorTest.java
new file mode 100644
index 000000000..11d67e7e5
--- /dev/null
+++ b/uimaj-core/src/test/java/org/apache/uima/analysis_component/JCasProcessorAnnotatorTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.uima.analysis_component;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import org.apache.uima.analysis_engine.AnalysisEngine;
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.util.CasCreationUtils;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class JCasProcessorAnnotatorTest {
+
+ private static final String NEEDLE = "needle";
+
+ private CAS cas;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ cas = CasCreationUtils.createCas();
+ }
+
+ @Test
+ void thatProcessingWithJCasInterfaceWorks() throws Exception {
+ AnalysisEngine engine = JCasProcessorAnnotator.of(_cas -> _cas.setDocumentText(NEEDLE));
+ engine.process(cas.getJCas());
+
+ assertThat(cas.getDocumentText()).isEqualTo(NEEDLE);
+ }
+
+ @Test
+ void thatProcessingWithCasInterfaceWorks() throws Exception {
+ AnalysisEngine engine = JCasProcessorAnnotator.of(_cas -> _cas.setDocumentText(NEEDLE));
+ engine.process(cas);
+
+ assertThat(cas.getDocumentText()).isEqualTo(NEEDLE);
+ }
+
+ @Test
+ void thatAnalysisEngineProcessExceptionMakesItThrough() throws Exception {
+ AnalysisEngine engine = JCasProcessorAnnotator.of(_cas -> {
+ throw new AnalysisEngineProcessException(NEEDLE, null);
+ });
+
+ assertThatExceptionOfType(AnalysisEngineProcessException.class)
+ .isThrownBy(() -> engine.process(cas)) //
+ .matches(ex -> NEEDLE.equals(ex.getMessageKey()));
+ }
+
+ @Test
+ void thatGenericExceptionIsWrapped() throws Exception {
+ AnalysisEngine engine = JCasProcessorAnnotator.of(_cas -> {
+ throw new RuntimeException(NEEDLE);
+ });
+
+ Assertions.setMaxStackTraceElementsDisplayed(1000);
+ assertThatExceptionOfType(AnalysisEngineProcessException.class)
+ .isThrownBy(() -> engine.process(cas)) //
+ .matches(ex -> ex.getCause() instanceof RuntimeException)
+ .matches(ex -> NEEDLE.equals(ex.getCause().getMessage()));
+ }
+}