Skip to content

Commit

Permalink
Issue #385: Allow import of type systems published through SPI
Browse files Browse the repository at this point in the history
- Improve performance of CAS creation
  • Loading branch information
reckart committed Nov 15, 2024
1 parent 35a2f25 commit 691c247
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.methodType;
import static java.util.Collections.emptyMap;

import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -393,7 +394,7 @@ private static void loadBuiltins(TypeImpl ti, ClassLoader cl,

if (BuiltinTypeKinds.creatableBuiltinJCas.contains(typeName)
|| typeName.equals(CAS.TYPE_NAME_SOFA)) {
JCasClassInfo jcci = getOrCreateJCasClassInfo(ti, cl, type2jcci, defaultLookup);
JCasClassInfo jcci = getOrCreateJCasClassInfo(ti, cl, type2jcci, emptyMap());
assert jcci != null;
// done while beginning to commit the staticTsi (before committed flag is set), for builtins
updateOrValidateAllCallSitesForJCasClass(jcci.jcasClass, ti, callSites_toSync);
Expand Down Expand Up @@ -481,12 +482,12 @@ private static synchronized void loadJCasForTSandClassLoader(TypeSystemImpl ts,
*/
// @formatter:on

ArrayList<MutableCallSite> callSites_toSync = new ArrayList<>();
var spiJCasClasses = loadJCasClassesFromSPI(cl);
var callSites_toSync = new ArrayList<MutableCallSite>();
maybeLoadJCasAndSubtypes(ts, ts.topType, type2jcci.get(TOP.class.getCanonicalName()), cl,
type2jcci, callSites_toSync);
type2jcci, callSites_toSync, spiJCasClasses);

MutableCallSite[] sync = callSites_toSync
.toArray(new MutableCallSite[callSites_toSync.size()]);
var sync = callSites_toSync.toArray(new MutableCallSite[callSites_toSync.size()]);
MutableCallSite.syncAll(sync);

checkConformance(ts, ts.topType, type2jcci);
Expand Down Expand Up @@ -531,9 +532,10 @@ private static void setTypeFromJCasIDforBuiltIns(JCasClassInfo jcci, TypeSystemI
// @formatter:on
private static void maybeLoadJCasAndSubtypes(TypeSystemImpl tsi, TypeImpl ti,
JCasClassInfo copyDownDefault_jcasClassInfo, ClassLoader cl,
Map<String, JCasClassInfo> type2jcci, ArrayList<MutableCallSite> callSites_toSync) {
Map<String, JCasClassInfo> type2jcci, ArrayList<MutableCallSite> callSites_toSync,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {

var jcci = getOrCreateJCasClassInfo(ti, cl, type2jcci, null);
var jcci = getOrCreateJCasClassInfo(ti, cl, type2jcci, aSpiJCasClasses);

if (null != jcci && tsi.isCommitted()) {
updateOrValidateAllCallSitesForJCasClass(jcci.jcasClass, ti, callSites_toSync);
Expand Down Expand Up @@ -623,7 +625,8 @@ private static void maybeLoadJCasAndSubtypes(TypeSystemImpl tsi, TypeImpl ti,
}

for (var subType : ti.getDirectSubtypes()) {
maybeLoadJCasAndSubtypes(tsi, subType, jcci_or_copyDown, cl, type2jcci, callSites_toSync);
maybeLoadJCasAndSubtypes(tsi, subType, jcci_or_copyDown, cl, type2jcci, callSites_toSync,
aSpiJCasClasses);
}
}

Expand All @@ -638,17 +641,36 @@ private static void maybeLoadJCasAndSubtypes(TypeSystemImpl tsi, TypeImpl ti,
*
* @return jcci or null, if no JCas class for this type was able to be loaded
*
* @deprecated This will become a package private method.
* @deprecated This will be removed without replacement
* @forRemoval 4.0.0
*/
@Deprecated(since = "3.5.1")
public static JCasClassInfo getOrCreateJCasClassInfo(TypeImpl aTypeInfo, ClassLoader aClassLoader,
Map<String, JCasClassInfo> aType2Jcci, Lookup aUnusedLookup) {

return getOrCreateJCasClassInfo(aTypeInfo, aClassLoader, aType2Jcci,
loadJCasClassesFromSPI(aClassLoader));
}

/**
* For a particular type name, get the JCasClassInfo
* <ul>
* <li>by fetching the cached value
* <li>by loading the class
* <li>return null if no JCas class for this name
* </ul>
* only called for non-Pear callers
*
* @return jcci or null, if no JCas class for this type was able to be loaded
*/
static JCasClassInfo getOrCreateJCasClassInfo(TypeImpl aTypeInfo, ClassLoader aClassLoader,
Map<String, JCasClassInfo> aType2Jcci,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {

var jcci = aType2Jcci.get(aTypeInfo.getJCasClassName());

if (jcci == null) {
jcci = maybeCreateJCasClassInfo(aTypeInfo, aClassLoader, aType2Jcci, aUnusedLookup);
jcci = maybeCreateJCasClassInfo(aTypeInfo, aClassLoader, aType2Jcci, aSpiJCasClasses);
}

// do this setup for new type systems using previously loaded jcci, as well as
Expand All @@ -665,10 +687,10 @@ public static JCasClassInfo getOrCreateJCasClassInfo(TypeImpl aTypeInfo, ClassLo
*/
@Deprecated(since = "3.5.1")
static JCasClassInfo maybeCreateJCasClassInfo(TypeImpl ti, ClassLoader cl,
Map<String, JCasClassInfo> type2jcci, Lookup aUnusedLookup) {
Map<String, JCasClassInfo> type2jcci, Map<String, Class<? extends TOP>> aSpiJCasClasses) {

// does update of callsites if was able find JCas class
var jcci = createJCasClassInfo(ti, cl, aUnusedLookup);
var jcci = createJCasClassInfo(ti, cl, aSpiJCasClasses);

if (null != jcci) {
type2jcci.put(ti.getJCasClassName(), jcci);
Expand All @@ -678,14 +700,19 @@ static JCasClassInfo maybeCreateJCasClassInfo(TypeImpl ti, ClassLoader cl,
}

/**
* @deprecated This will become a private method.
* @deprecated This will be removed without replacement.
* @forRemoval 4.0.0
*/
@SuppressWarnings("javadoc")
@Deprecated(since = "3.5.1")
public static JCasClassInfo createJCasClassInfo(TypeImpl aTypeInfo, ClassLoader aClassLoader,
Lookup aUnusedLookup) {
var jcasClass = maybeJCasWrapperClass(aTypeInfo, aClassLoader);
return createJCasClassInfo(aTypeInfo, aClassLoader, loadJCasClassesFromSPI(aClassLoader));
}

private static JCasClassInfo createJCasClassInfo(TypeImpl aTypeInfo, ClassLoader aClassLoader,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {
var jcasClass = maybeJCasWrapperClass(aTypeInfo, aClassLoader, aSpiJCasClasses);
if (null == jcasClass || !TOP.class.isAssignableFrom(jcasClass)) {
return null;
}
Expand All @@ -705,7 +732,6 @@ public static JCasClassInfo createJCasClassInfo(TypeImpl aTypeInfo, ClassLoader
var lookup = getLookup(jcasClass.getClassLoader());
return createJCasClassInfo(jcasClass, aTypeInfo, jcasType, lookup);
}

// static AtomicLong time = IS_TIME_AUGMENT_FEATURES ? new AtomicLong(0) : null;
//
// static {
Expand Down Expand Up @@ -957,7 +983,8 @@ private static String getAllSuperTypeNames(TypeImpl ti) {
* @return the loaded / resolved class
*/
@SuppressWarnings("unchecked")
private static Class<? extends TOP> maybeJCasWrapperClass(TypeImpl ti, ClassLoader cl) {
private static Class<? extends TOP> maybeJCasWrapperClass(TypeImpl ti, ClassLoader cl,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {
var className = ti.getJCasClassName();

// First we try the local classloader - this is necessary because it might be a PEAR situation
Expand All @@ -970,7 +997,7 @@ private static Class<? extends TOP> maybeJCasWrapperClass(TypeImpl ti, ClassLoad
}

// If the local classloader does not have the JCas wrapper, we try the SPI
return loadJCasClassesFromSPI(cl).get(className);
return aSpiJCasClasses.get(className);
}

static Map<String, Class<? extends TOP>> loadJCasClassesFromSPI(ClassLoader cl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1479,9 +1479,11 @@ private TypeSystemImpl finalizeCommit(ClassLoader cl) {

type2jcci = FSClassRegistry.get_className_to_jcci(cl, false /* is not PEAR */);
lookup = FSClassRegistry.getLookup(cl);
var spiJCasClasses = FSClassRegistry.loadJCasClassesFromSPI(cl);
cl_for_commit = cl;

computeAdjustedFeatureOffsets(topType); // must preceed the FSClassRegistry JCas stuff below
computeAdjustedFeatureOffsets(topType, spiJCasClasses); // must preceed the FSClassRegistry JCas
// stuff below

// Load all the available JCas classes (if not already loaded).
// Has to follow above, because information computed above is used when
Expand Down Expand Up @@ -1529,7 +1531,8 @@ private TypeSystemImpl finalizeCommit(ClassLoader cl) {
* - the type
*/
//@formatter:on
private void computeAdjustedFeatureOffsets(TypeImpl ti) {
private void computeAdjustedFeatureOffsets(TypeImpl ti,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {

List<FeatureImpl> tempIntFis = new ArrayList<>();
List<FeatureImpl> tempRefFis = new ArrayList<>();
Expand All @@ -1549,10 +1552,10 @@ private void computeAdjustedFeatureOffsets(TypeImpl ti) {
// this includes any superClass of a jcas class which is not in this type system

if (!skip_loading_user_jcas) {
JCasClassInfo jcci = getOrCreateJcci(ti);
JCasClassInfo jcci = getOrCreateJcci(ti, aSpiJCasClasses);

if (jcci != null) {
addJCasOffsetsWithSupers(jcci.jcasClass, tempIntFis, tempRefFis, tempNsr);
addJCasOffsetsWithSupers(jcci.jcasClass, tempIntFis, tempRefFis, tempNsr, aSpiJCasClasses);
}
}

Expand Down Expand Up @@ -1582,7 +1585,7 @@ private void computeAdjustedFeatureOffsets(TypeImpl ti) {
// ti.hasNoSlots = ti.nbrOfUsedIntDataSlots == 0 && ti.nbrOfUsedRefDataSlots == 0;

for (TypeImpl sub : ti.getDirectSubtypes()) {
computeAdjustedFeatureOffsets(sub);
computeAdjustedFeatureOffsets(sub, aSpiJCasClasses);
}
}

Expand Down Expand Up @@ -1610,7 +1613,8 @@ private void computeAdjustedFeatureOffsets(TypeImpl ti) {
*/
//@formatter:on
private void addJCasOffsetsWithSupers(Class<?> clazz, List<FeatureImpl> tempIntFis,
List<FeatureImpl> tempRefFis, List<FeatureImpl> tempNsrFis) {
List<FeatureImpl> tempRefFis, List<FeatureImpl> tempNsrFis,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {

Class<?> superClass = clazz.getSuperclass();
// **********************
Expand All @@ -1634,7 +1638,7 @@ private void addJCasOffsetsWithSupers(Class<?> clazz, List<FeatureImpl> tempIntF
String uimaTypeName = Misc.javaClassName2UimaTypeName(className);
TypeImpl ti = getType(uimaTypeName);
if (ti != null) {
maybeAddJCasOffsets(ti, tempIntFis, tempRefFis, tempNsrFis);
maybeAddJCasOffsets(ti, tempIntFis, tempRefFis, tempNsrFis, aSpiJCasClasses);
}
return;
}
Expand All @@ -1643,7 +1647,7 @@ private void addJCasOffsetsWithSupers(Class<?> clazz, List<FeatureImpl> tempIntF
// If they exist, they will have been loaded/created by FSClassRegistry.augmentFeaturesFromJCas
// some intermediate superclasses may not have jccis, just skip over them.

addJCasOffsetsWithSupers(superClass, tempIntFis, tempRefFis, tempNsrFis);
addJCasOffsetsWithSupers(superClass, tempIntFis, tempRefFis, tempNsrFis, aSpiJCasClasses);
// maybeAddJCasOffsets(clazz, tempIntFis, tempRefFis, tempNsrFis); // already done by above
// statement
}
Expand All @@ -1661,9 +1665,10 @@ private void addJCasOffsetsWithSupers(Class<?> clazz, List<FeatureImpl> tempIntF
* list to augment with additional slots
*/
private void maybeAddJCasOffsets(TypeImpl ti, List<FeatureImpl> tempIntFis,
List<FeatureImpl> tempRefFis, List<FeatureImpl> tempNsrFis) {
List<FeatureImpl> tempRefFis, List<FeatureImpl> tempNsrFis,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {

JCasClassInfo jcci = getOrCreateJcci(ti);
JCasClassInfo jcci = getOrCreateJcci(ti, aSpiJCasClasses);
if (null != jcci) { // could be null if class is not a JCas class
addJCasOffsets(jcci, tempIntFis, tempRefFis, tempNsrFis);
}
Expand Down Expand Up @@ -1735,8 +1740,9 @@ void setOffset2Feat(List<FeatureImpl> tempIntFis, List<FeatureImpl> tempRefFis,
}
}

private JCasClassInfo getOrCreateJcci(TypeImpl ti) {
return FSClassRegistry.getOrCreateJCasClassInfo(ti, cl_for_commit, type2jcci, lookup);
private JCasClassInfo getOrCreateJcci(TypeImpl ti,
Map<String, Class<? extends TOP>> aSpiJCasClasses) {
return FSClassRegistry.getOrCreateJCasClassInfo(ti, cl_for_commit, type2jcci, aSpiJCasClasses);
}

JCasClassInfo getJcci(String typeName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.InstanceOfAssertFactories.throwable;

import java.lang.invoke.MethodHandles;
import java.lang.management.ManagementFactory;

import org.apache.uima.cas.CAS;
Expand All @@ -36,12 +37,16 @@
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import x.y.z.Sentence;

class TypeSystemImplTest {
private TypeSystemDescription tsd;

static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

@BeforeEach
void setup() {
tsd = getResourceSpecifierFactory().createTypeSystemDescription();
Expand Down Expand Up @@ -109,7 +114,12 @@ void testMetaspaceExhaustion() throws Exception {
var type = tsd.addType(Sentence._TypeName, "", CAS.TYPE_NAME_ANNOTATION);
type.addFeature(Sentence._FeatName_sentenceLength, null, CAS.TYPE_NAME_INTEGER);

var target = threshold * 2;
for (var i = 0; i < threshold * 2; i++) {
if ((i + 1) % 250 == 0) {
LOG.info("Metaspace exhaustion test - progress: {} / {}", i + 1, target);
}

var resMgr = new ResourceManager_impl();
resMgr.setExtensionClassPath(".", false);
createCas(tsd, null, null, null, resMgr).getJCas();
Expand Down
2 changes: 2 additions & 0 deletions uimaj-core/src/test/resources/log4j2-test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
</Console>
</Appenders>
<Loggers>
<Logger name="org.apache.uima.cas.impl.TypeSystemImplTest" level="INFO" />

<Root level="WARN">
<AppenderRef ref="Console"/>
</Root>
Expand Down

0 comments on commit 691c247

Please sign in to comment.