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 2020/11/02 13:39:44 UTC
[uima-uimaj] 01/01: [UIMA-6287] FSArray.spliterator() fails to
handle PEAR case
This is an automated email from the ASF dual-hosted git repository.
rec pushed a commit to branch bugfix/UIMA-6287-FSArray.spliterator-fails-to-handle-PEAR-case
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git
commit c181642a479a71ef91180fd77fb8008961c269e2
Author: Richard Eckart de Castilho <re...@apache.org>
AuthorDate: Mon Nov 2 14:39:34 2020 +0100
[UIMA-6287] FSArray.spliterator() fails to handle PEAR case
- Added unit test for the issue that the spliterator is returning the wrong type
- Refactored other JCas classloading tests a bit in the process
---
.../apache/uima/cas/test/JCasClassLoaderTest.java | 240 +++++++++++++++++----
1 file changed, 201 insertions(+), 39 deletions(-)
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/test/JCasClassLoaderTest.java b/uimaj-core/src/test/java/org/apache/uima/cas/test/JCasClassLoaderTest.java
index b192e44..ef21e5d 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/test/JCasClassLoaderTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/test/JCasClassLoaderTest.java
@@ -24,7 +24,10 @@ import static org.apache.uima.UIMAFramework.getResourceSpecifierFactory;
import static org.apache.uima.UIMAFramework.getXMLParser;
import static org.apache.uima.UIMAFramework.newDefaultResourceManager;
import static org.apache.uima.UIMAFramework.produceAnalysisEngine;
+import static org.apache.uima.cas.CAS.TYPE_NAME_ANNOTATION;
+import static org.apache.uima.cas.CAS.TYPE_NAME_FS_ARRAY;
import static org.apache.uima.util.CasCreationUtils.createCas;
+import static org.apache.uima.util.CasCreationUtils.mergeTypeSystems;
import java.io.File;
import java.io.IOException;
@@ -41,6 +44,7 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -51,12 +55,15 @@ import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
import org.apache.uima.analysis_engine.impl.PrimitiveAnalysisEngine_impl;
import org.apache.uima.cas.CAS;
-import org.apache.uima.cas.CASException;
+import org.apache.uima.cas.FeatureStructure;
import org.apache.uima.cas.impl.CASImpl;
+import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.jcas.JCas;
+import org.apache.uima.jcas.cas.FSArray;
import org.apache.uima.jcas.tcas.Annotation;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
+import org.apache.uima.resource.metadata.TypeDescription;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.util.InvalidXMLException;
import org.apache.uima.util.XMLInputSource;
@@ -66,25 +73,38 @@ import org.junit.Test;
public class JCasClassLoaderTest {
- public static Object casTokenClass;
- public static Object addTokenAETokenClass;
- public static Object fetchTokenAETokenClass;
- public static Object indexedTokenClass;
+ public static final String TYPE_NAME_TOKEN = Token.class.getName();
+ public static final String TYPE_NAME_ARRAY_HOST = "uima.testing.ArrayHost";
+ public static final String FEAT_NAME_ARRAY_HOST_VALUES = "values";
+
+ public static Class casTokenClassViaClassloader;
+ public static Class casTokenClassViaCas;
+ public static Class addTokenAETokenClass;
+ public static Class fetchTokenAETokenClass;
+ public static Class indexedTokenClass;
public static boolean fetchThrowsClassCastException;
-
+
+ public static Class tokenClassAddedToArray;
+ public static Class tokenClassFetchedFromArray;
+
@Before
public void setup()
{
- casTokenClass = null;
+ casTokenClassViaClassloader = null;
+ casTokenClassViaCas = null;
addTokenAETokenClass = null;
fetchTokenAETokenClass = null;
+ indexedTokenClass = null;
fetchThrowsClassCastException = false;
+
+ tokenClassAddedToArray = null;
+ tokenClassFetchedFromArray = null;
}
/**
* This test simulates an environment as it could exist when using e.g. PEARs. We use a vanilla
- * JCas and the analysis engines each use local JCas wrappers which are provided as an extra classpath
- * passed to the resource manager.
+ * JCas and the analysis engines each use local JCas wrappers which are provided as an extra
+ * classpath passed to the resource manager.
*
* <ul>
* <li>JCas does not know JCas wrappers for the {@code Token} type.</li>
@@ -105,9 +125,8 @@ public class JCasClassLoaderTest {
ClassLoader rootCl = getClass().getClassLoader();
// We do not want the CAS to know the Token JCas wrapper when it gets initialized
- ClassLoader clForCas = new IsolatingClassloader("CAS", rootCl)
+ IsolatingClassloader clForCas = new IsolatingClassloader("CAS", rootCl)
.hiding("org\\.apache\\.uima\\.cas\\.test\\.Token(_Type)?.*");
-
File cpBase = new File("target/test-output/JCasClassLoaderTest/classes");
File cpPackageBase = new File(cpBase, "org/apache/uima/cas/test");
@@ -134,7 +153,8 @@ public class JCasClassLoaderTest {
fetchTheTokenAnnotator.process(jcas);
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
- softly.assertThat(casTokenClass).isNull();
+ softly.assertThat(casTokenClassViaClassloader).isNull();
+ softly.assertThat(casTokenClassViaCas).isSameAs(Annotation.class);
softly.assertThat(addTokenAETokenClass).isNotNull();
softly.assertThat(fetchTokenAETokenClass).isNotNull();
softly.assertThat(addTokenAETokenClass)
@@ -173,7 +193,7 @@ public class JCasClassLoaderTest {
ClassLoader rootCl = getClass().getClassLoader();
// We do not want the CAS to know the Token JCas wrapper when it gets initialized
- ClassLoader clForCas = new IsolatingClassloader("CAS", rootCl)
+ IsolatingClassloader clForCas = new IsolatingClassloader("CAS", rootCl)
.hiding("org\\.apache\\.uima\\.cas\\.test\\.Token(_Type)?.*");
ClassLoader clForAddATokenAnnotator = new IsolatingClassloader("AddATokenAnnotator", rootCl)
@@ -197,7 +217,8 @@ public class JCasClassLoaderTest {
fetchTheTokenAnnotator.process(jcas);
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
- softly.assertThat(casTokenClass).isNull();
+ softly.assertThat(casTokenClassViaClassloader).isNull();
+ softly.assertThat(casTokenClassViaCas).isSameAs(Annotation.class);
softly.assertThat(addTokenAETokenClass).isNotNull();
softly.assertThat(fetchTokenAETokenClass).isNotNull();
softly.assertThat(addTokenAETokenClass)
@@ -236,7 +257,7 @@ public class JCasClassLoaderTest {
public void thatAnnotatorsCanLocallyUseDifferentJCasWrappers() throws Exception {
ClassLoader rootCl = getClass().getClassLoader();
- ClassLoader clForCas = new IsolatingClassloader("CAS", rootCl);
+ IsolatingClassloader clForCas = new IsolatingClassloader("CAS", rootCl);
ClassLoader clForAddATokenAnnotator = new IsolatingClassloader("AddATokenAnnotator", rootCl)
.redefining("^.*AddATokenAnnotator$")
@@ -259,13 +280,14 @@ public class JCasClassLoaderTest {
fetchTheTokenAnnotator.process(jcas);
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
- softly.assertThat(casTokenClass).isNotNull();
+ softly.assertThat(casTokenClassViaClassloader).isNotNull();
+ softly.assertThat(casTokenClassViaCas).isSameAs(Token.class);
softly.assertThat(addTokenAETokenClass).isNotNull();
softly.assertThat(fetchTokenAETokenClass).isNotNull();
- softly.assertThat(casTokenClass)
+ softly.assertThat(casTokenClassViaClassloader)
.as("JCas and AddTokenAnnotator use different Token wrappers")
.isNotEqualTo(addTokenAETokenClass);
- softly.assertThat(casTokenClass)
+ softly.assertThat(casTokenClassViaClassloader)
.as("JCas and FetchTokenAnnotator use different Token wrappers")
.isNotEqualTo(fetchTokenAETokenClass);
softly.assertThat(addTokenAETokenClass)
@@ -316,7 +338,7 @@ public class JCasClassLoaderTest {
public void thatTypeSystemCanComeFromItsOwnClassLoader() throws Exception {
ClassLoader rootCl = getClass().getClassLoader();
- ClassLoader clForCas = new IsolatingClassloader("CAS", rootCl)
+ IsolatingClassloader clForCas = new IsolatingClassloader("CAS", rootCl)
.hiding("org\\.apache\\.uima\\.cas\\.test\\.Token(_Type)?.*");
ClassLoader clForTS = new IsolatingClassloader("TS", rootCl)
@@ -343,13 +365,14 @@ public class JCasClassLoaderTest {
fetchTheTokenAnnotator.process(jcas);
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
- softly.assertThat(casTokenClass).isNull();
+ softly.assertThat(casTokenClassViaClassloader).isNull();
+ softly.assertThat(casTokenClassViaCas).isSameAs(Annotation.class);
softly.assertThat(addTokenAETokenClass).isNotNull();
softly.assertThat(fetchTokenAETokenClass).isNotNull();
- softly.assertThat(casTokenClass)
+ softly.assertThat(casTokenClassViaClassloader)
.as("JCas and AddTokenAnnotator use different Token wrappers")
.isNotEqualTo(addTokenAETokenClass);
- softly.assertThat(casTokenClass)
+ softly.assertThat(casTokenClassViaClassloader)
.as("JCas and FetchTokenAnnotator use different Token wrappers")
.isNotEqualTo(fetchTokenAETokenClass);
softly.assertThat(addTokenAETokenClass)
@@ -363,18 +386,64 @@ public class JCasClassLoaderTest {
.isFalse();
}
}
+
+ @Test
+ public void thatFSArraySpliteratorReturnsProperJCasWrapper() throws Exception {
+ ClassLoader rootCl = getClass().getClassLoader();
+
+ IsolatingClassloader clForCas = new IsolatingClassloader("CAS", rootCl)
+ .hiding("org\\.apache\\.uima\\.cas\\.test\\.Token(_Type)?.*");
+
+ ClassLoader clForCreators = new IsolatingClassloader("Creators", rootCl)
+ .hiding("org\\.apache\\.uima\\.cas\\.test\\.Token(_Type)?.*")
+ .redefining("^.*AddATokenAnnotatorNoJCas$")
+ .redefining("^.*AddTokenToArrayAnnotatorNoJCas$");
+
+ ClassLoader clForAccessors = new IsolatingClassloader("Accessors", rootCl)
+ .redefining("org\\.apache\\.uima\\.cas\\.test\\.Token(_Type)?.*")
+ .redefining("^.*FetchTokenFromArrayViaSpliteratorAnnotator$");
+
+ TypeSystemDescription tsd = mergeTypeSystems(
+ asList(loadTokensAndSentencesTS(), makeArrayTestTS()));
+
+ JCas jcas = makeJCas(clForCas, tsd);
+ AnalysisEngine addATokenAnnotator = makeAnalysisEngine(AddATokenAnnotatorNoJCas.class,
+ clForCreators);
+ AnalysisEngine addTokenToArrayAnnotator = makeAnalysisEngine(AddTokenToArrayAnnotatorNoJCas.class,
+ clForCreators);
+ AnalysisEngine fetchTokenFromArrayViaSpliteratorAnnotator = makeAnalysisEngine(
+ FetchTokenFromArrayViaSpliteratorAnnotator.class, clForAccessors);
+
+ jcas.setDocumentText("test");
+
+ addATokenAnnotator.process(jcas);
+ addTokenToArrayAnnotator.process(jcas);
+ fetchTokenFromArrayViaSpliteratorAnnotator.process(jcas);
+
+ try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
+ softly.assertThat(casTokenClassViaClassloader).isNull();
+ softly.assertThat(casTokenClassViaCas).isSameAs(Annotation.class);
+ softly.assertThat(addTokenAETokenClass).isNotNull();
+ softly.assertThat(casTokenClassViaClassloader)
+ .as("JCas and AddTokenAnnotator use different Token wrappers")
+ .isNotEqualTo(addTokenAETokenClass);
+ softly.assertThat(tokenClassFetchedFromArray.getName())
+ .as("Array spliterator returns proper Token wrapper")
+ .isEqualTo(TYPE_NAME_TOKEN);
+ }
+ }
- private static Class<?> loadTokenClass(ClassLoader cl)
+ public static Class<?> loadTokenClass(ClassLoader cl)
{
try {
- return cl.loadClass(Token.class.getName());
+ return cl.loadClass(TYPE_NAME_TOKEN);
} catch (ClassNotFoundException e) {
return null;
}
}
- private static void printTokenClassLoaderInfo(String context, ClassLoader cl) {
+ public static void printTokenClassLoaderInfo(String context, ClassLoader cl) {
Class<?> clazz = loadTokenClass(cl);
if (clazz != null) {
System.out.printf("[%s] %s %d %n", context, clazz.getName(), clazz.hashCode());
@@ -382,21 +451,36 @@ public class JCasClassLoaderTest {
System.out.printf("[%s] %s NOT AVAILABLE %n", context, Token.class.getName());
}
}
+
+ private TypeSystemDescription makeArrayTestTS() throws InvalidXMLException, IOException
+ {
+ TypeSystemDescription tsd = getResourceSpecifierFactory().createTypeSystemDescription();
+ TypeDescription arrayHost = tsd.addType(TYPE_NAME_ARRAY_HOST, "", TYPE_NAME_ANNOTATION);
+ arrayHost.addFeature(FEAT_NAME_ARRAY_HOST_VALUES, "", TYPE_NAME_FS_ARRAY,
+ TYPE_NAME_ANNOTATION, true);
+ return tsd;
+ }
+
+ private TypeSystemDescription loadTokensAndSentencesTS() throws InvalidXMLException, IOException
+ {
+ return getXMLParser().parseTypeSystemDescription(new XMLInputSource(
+ new File("src/test/resources/CASTests/desc/TokensAndSentencesTS.xml")));
+ }
/**
* Creates a new JCas and sets it up so it uses the given classloader to load its JCas wrappers.
*/
- private JCas makeJCas(ClassLoader cl)
- throws ResourceInitializationException, CASException, InvalidXMLException, IOException {
- printTokenClassLoaderInfo("JCas creation", cl);
- casTokenClass = loadTokenClass(cl);
- TypeSystemDescription tsd = getXMLParser().parseTypeSystemDescription(new XMLInputSource(
- new File("src/test/resources/CASTests/desc/TokensAndSentencesTS.xml")));
- ResourceManager resMgr = newDefaultResourceManager();
- // resMgr.setExtensionClassPath(cl, "", false);
- CASImpl cas = (CASImpl) createCas(tsd, null, null, null, resMgr);
- cas.setJCasClassLoader(cl);
- return cas.getJCas();
+ private JCas makeJCas(IsolatingClassloader cl)
+ throws Exception {
+ return makeJCas(cl, loadTokensAndSentencesTS());
+ }
+
+ private JCas makeJCas(IsolatingClassloader cl, TypeSystemDescription tsd)
+ throws Exception {
+ cl.redefining("^.*JCasCreatorImpl$");
+ Class jcasCreatorClass = cl.loadClass(JCasCreatorImpl.class.getName());
+ JCasCreator creator = (JCasCreator) jcasCreatorClass.newInstance();
+ return creator.createJCas(cl, tsd);
}
/**
@@ -406,7 +490,7 @@ public class JCasClassLoaderTest {
private AnalysisEngine makeAnalysisEngine(Class<? extends Annotator_ImplBase> aeClass,
ClassLoader cl) throws ResourceInitializationException, MalformedURLException {
ResourceManager resMgr = newDefaultResourceManager();
- resMgr.setExtensionClassPath(cl, "", false);
+ resMgr.setExtensionClassLoader(cl, false);
printTokenClassLoaderInfo("AE creation: " + aeClass.getSimpleName(), resMgr.getExtensionClassLoader());
AnalysisEngineDescription desc = getResourceSpecifierFactory()
.createAnalysisEngineDescription();
@@ -437,12 +521,89 @@ public class JCasClassLoaderTest {
public void process(JCas aJCas) throws AnalysisEngineProcessException {
addTokenAETokenClass = Token.class;
System.out.printf("%s class loader: %s%n", getClass().getName(), getClass().getClassLoader());
- System.out.printf("[AE runtime: %s] %s %d %n", getClass().getName(), Token.class.getName(),
- Token.class.hashCode());
+ System.out.printf("[AE runtime: %s] %s %d %n", getClass().getName(),
+ addTokenAETokenClass.getName(), addTokenAETokenClass.hashCode());
new Token(aJCas, 0, aJCas.getDocumentText().length()).addToIndexes();
}
}
+
+ public static interface JCasCreator {
+ JCas createJCas(ClassLoader cl, TypeSystemDescription tsd);
+ }
+
+ public static class JCasCreatorImpl implements JCasCreator {
+
+ @Override
+ public JCas createJCas(ClassLoader cl, TypeSystemDescription tsd)
+ {
+ try {
+ printTokenClassLoaderInfo("JCas creation", cl);
+ casTokenClassViaClassloader = loadTokenClass(cl);
+ ResourceManager resMgr = newDefaultResourceManager();
+ resMgr.setExtensionClassLoader(cl, false);
+ CASImpl cas = (CASImpl) createCas(tsd, null, null, null, resMgr);
+ cas.setJCasClassLoader(cl);
+
+ casTokenClassViaCas = cas
+ .createAnnotation(cas.getTypeSystem().getType(TYPE_NAME_TOKEN), 0, 0).getClass();
+
+ return cas.getJCas();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static class AddATokenAnnotatorNoJCas extends JCasAnnotator_ImplBase {
+ @Override
+ public void process(JCas aJCas) throws AnalysisEngineProcessException {
+ AnnotationFS token = aJCas.getCas().createAnnotation(
+ aJCas.getTypeSystem().getType(TYPE_NAME_TOKEN), 0,
+ aJCas.getDocumentText().length());
+ addTokenAETokenClass = token.getClass();
+ System.out.printf("[AE runtime: %s] CAS class loader: %s%n", getClass().getName(), aJCas.getCasImpl().getJCasClassLoader());
+ System.out.printf("[AE runtime: %s] AE class loader: %s%n", getClass().getName(), getClass().getClassLoader());
+ System.out.printf("[AE runtime: %s] %s %d loaded by %s%n", getClass().getName(), addTokenAETokenClass.getName(),
+ addTokenAETokenClass.hashCode(), addTokenAETokenClass.getClassLoader());
+ aJCas.getCas().addFsToIndexes(token);
+ }
+ }
+
+ public static class AddTokenToArrayAnnotatorNoJCas extends JCasAnnotator_ImplBase {
+ @Override
+ public void process(JCas aJCas) throws AnalysisEngineProcessException {
+
+ AnnotationFS token = (AnnotationFS) aJCas
+ .select(aJCas.getTypeSystem().getType(TYPE_NAME_TOKEN))
+ .single();
+ tokenClassAddedToArray = token.getClass();
+
+ AnnotationFS arrayHost = aJCas.getCas()
+ .createAnnotation(aJCas.getTypeSystem().getType(TYPE_NAME_ARRAY_HOST), 0, 0);
+ FSArray array = new FSArray<>(aJCas, 1);
+ array.set(0, token);
+ arrayHost.setFeatureValue(
+ arrayHost.getType().getFeatureByBaseName(FEAT_NAME_ARRAY_HOST_VALUES), array);
+ aJCas.getCas().addFsToIndexes(arrayHost);
+ }
+ }
+
+ public static class FetchTokenFromArrayViaSpliteratorAnnotator extends JCasAnnotator_ImplBase {
+ @Override
+ public void process(JCas aJCas) throws AnalysisEngineProcessException {
+ FeatureStructure arrayHost = aJCas.select(aJCas.getTypeSystem().getType(TYPE_NAME_ARRAY_HOST))
+ .single();
+
+ FSArray array = (FSArray) arrayHost.getFeatureValue(
+ arrayHost.getType().getFeatureByBaseName(FEAT_NAME_ARRAY_HOST_VALUES));
+
+ tokenClassFetchedFromArray = StreamSupport.stream(array.spliterator(), false)
+ .findFirst()
+ .get().getClass();
+ }
+ }
public static class FetchTheTokenAnnotator extends JCasAnnotator_ImplBase {
@Override
@@ -524,6 +685,7 @@ public class JCasClassLoaderTest {
}
if (hideClassesPatterns.stream().anyMatch(name::matches)) {
+ System.out.printf("[%s] prevented access to hidden class: %s%n", id, name);
throw new ClassNotFoundException(name);
}