Skip to content

Commit 57aa798

Browse files
committed
Issue #382: Warning when PEAR contains a JCAS class that is used as a feature range outside the PEAR
- Do not issue warning on range mismatch if range class was loaded from a PEAR classloader - Clean up
1 parent a1b531b commit 57aa798

File tree

4 files changed

+29
-52
lines changed

4 files changed

+29
-52
lines changed

uimaj-core/src/main/java/org/apache/uima/cas/impl/FSClassRegistry.java

+27-2
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@
115115
* via accessing/updating a thread local instance of TypeSystemImpl.SlotAllocate.
116116
*/
117117
// @formatter:on
118-
public abstract class FSClassRegistry { // abstract to prevent instantiating; this class only has
119-
// static methods
118+
// abstract to prevent instantiating; this class only has static methods
119+
public abstract class FSClassRegistry {
120120

121121
static final String RECORD_JCAS_CLASSLOADERS = "uima.record_jcas_classloaders";
122122
static final boolean IS_RECORD_JCAS_CLASSLOADERS = Misc
@@ -1371,6 +1371,31 @@ private static void checkConformance(Class<?> clazz, TypeSystemImpl tsi, TypeImp
13711371
if (rangeClass.getName().equals("org.apache.uima.jcas.cas.Sofa")
13721372
&& returnClass.getName().equals("org.apache.uima.cas.SofaFS")) {
13731373
// empty
1374+
} else if (rangeClass.getClassLoader() instanceof UIMAClassLoader) {
1375+
// This can happen if:
1376+
//
1377+
// * we are in a PEAR which contains a local JCas wrapper for type X
1378+
// * -AND- additionally the type X also exists at a global level
1379+
// * -AND- there is another global type Y with a field x of type X
1380+
// * -AND- the PEAR does -NOT- have a local JCas wrapper for Y
1381+
//
1382+
// In this case, the getter/setter range class for Y.x are not compatible with the X from
1383+
// the PEAR which we found in the ti.getJavaClass() because that field inconveniently
1384+
// always contains the latest JCas wrapper that was encountered.
1385+
//
1386+
// The thing is that the PEAR would not be able to call the Y.x getter/setter anyway
1387+
// because the PEAR cannot access the class Y (that is assuming the best practice
1388+
// that the PEAR should always locally include all JCas wrapper classes it directly uses).
1389+
// In order to use the Y.x getter/setter, the PEAR would need to have a compile-time
1390+
// dependency on Y and then it should be bundled in the PEAR. So if it is not in the PEAR,
1391+
// then the PEAR cannot use it and therefore the warning would be pointless.
1392+
//
1393+
// What the PEAR could do though is created instances of Y via the CAS interface and use
1394+
// the CAS API to get/set feature values. This is covered by the PEARs trampoline
1395+
// mechanism.
1396+
//
1397+
// So if the range class is defined by an UIMAClassLoader (i.e. has been locally loaded by
1398+
// a PEAR) then we ignore the mismatch.
13741399
} else {
13751400
// CAS type system type "{0}" (loaded by {1}) defines field "{2}" with range "{3}" (loaded
13761401
// by {4}), but JCas getter method is returning "{5}" (loaded by {6}) which is not a

uimaj-core/src/test/java/org/apache/uima/cas/test/JCasClassLoaderTest.java

-48
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,11 @@
3333

3434
import java.io.File;
3535
import java.io.IOException;
36-
import java.lang.invoke.MethodHandles;
3736
import java.net.MalformedURLException;
3837
import java.util.ArrayList;
3938
import java.util.stream.Stream;
4039
import java.util.stream.StreamSupport;
4140

42-
import org.apache.uima.UIMAFramework;
4341
import org.apache.uima.analysis_component.Annotator_ImplBase;
4442
import org.apache.uima.analysis_component.JCasAnnotator_ImplBase;
4543
import org.apache.uima.analysis_engine.AnalysisEngine;
@@ -57,22 +55,14 @@
5755
import org.apache.uima.resource.ResourceManager;
5856
import org.apache.uima.resource.metadata.TypeDescription;
5957
import org.apache.uima.resource.metadata.TypeSystemDescription;
60-
import org.apache.uima.spi.JCasClassProviderForTesting;
6158
import org.apache.uima.test.IsolatingClassloader;
6259
import org.apache.uima.util.InvalidXMLException;
6360
import org.apache.uima.util.XMLInputSource;
6461
import org.assertj.core.api.AutoCloseableSoftAssertions;
6562
import org.junit.jupiter.api.BeforeEach;
6663
import org.junit.jupiter.api.Test;
67-
import org.junit.jupiter.api.io.TempDir;
68-
import org.slf4j.Logger;
69-
import org.slf4j.LoggerFactory;
70-
71-
import x.y.z.Token;
72-
import x.y.z.TokenType;
7364

7465
public class JCasClassLoaderTest {
75-
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
7666

7767
public static final String TYPE_NAME_TOKEN = Token.class.getName();
7868
public static final String TYPE_NAME_ARRAY_HOST = "uima.testing.ArrayHost";
@@ -446,44 +436,6 @@ void thatFSArrayToArrayReturnsProperJCasWrapper() throws Exception {
446436
}
447437
}
448438

449-
@Test
450-
void thatFeatureRangeClassRedefinedInPearDoesNotCauseProblems(@TempDir File aTemp)
451-
throws Exception {
452-
var tsd = UIMAFramework.getXMLParser().parseTypeSystemDescription(
453-
new XMLInputSource("src/test/java/org/apache/uima/jcas/test/generatedx.xml"));
454-
455-
LOG.info("-- Base runtime context --------------------------------------------------");
456-
LOG.info("{} loaded using {}", Token.class, Token.class.getClassLoader());
457-
LOG.info("{} loaded using {}", TokenType.class, TokenType.class.getClassLoader());
458-
459-
LOG.info(
460-
"-- JCas/PEAR-like classloader context ----------------------------------------------");
461-
var rootCl = getClass().getClassLoader();
462-
var clForCas = new IsolatingClassloader("CAS Classloader", rootCl) //
463-
.redefining(TokenType.class) //
464-
.redefining(JCasClassProviderForTesting.class) //
465-
.redefining(JCasCreator.class);
466-
467-
var jcasCreatorClass = clForCas.loadClass(JCasCreatorImpl.class.getName());
468-
var creator = (JCasCreator) jcasCreatorClass.getDeclaredConstructor().newInstance();
469-
var jcas = creator.createJCas(clForCas, tsd);
470-
var cas = jcas.getCas();
471-
472-
var t = cas.createFS(cas.getTypeSystem().getType(Token.class.getName()));
473-
var tt = cas.createFS(cas.getTypeSystem().getType(TokenType.class.getName()));
474-
475-
LOG.info("{} loaded using {}", t.getClass(), t.getClass().getClassLoader());
476-
LOG.info("{} loaded using {}", tt.getClass(), tt.getClass().getClassLoader());
477-
478-
assertThat(t.getClass().getClassLoader()) //
479-
.isSameAs(Token.class.getClassLoader());
480-
481-
assertThat(tt.getClass().getClassLoader()) //
482-
.isSameAs(clForCas);
483-
484-
t.setFeatureValue(t.getType().getFeatureByBaseName(Token._FeatName_ttype), tt);
485-
}
486-
487439
public static Class<?> loadTokenClass(ClassLoader cl) {
488440
try {
489441
return cl.loadClass(TYPE_NAME_TOKEN);

uimaj-core/src/test/resources/log4j2-test.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
</Console>
2626
</Appenders>
2727
<Loggers>
28-
<Root level="info">
28+
<Root level="WARN">
2929
<AppenderRef ref="Console"/>
3030
</Root>
3131
</Loggers>
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
org.slf4j.simpleLogger.defaultLogLevel=debug
1+
org.slf4j.simpleLogger.defaultLogLevel=WARN

0 commit comments

Comments
 (0)