blob: 14ecb831bd44e70d6598f6546709bfd1b1995a94 [file] [log] [blame]
/*
* 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.cas.impl;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.uima.cas.FeatureStructure;
import org.apache.uima.cas.Type;
/*
* There is one instance of this class per type system.
* It is shared by multiple CASes (in a CAS pool, for instance,
* when these CASes are sharing the same type system), and
* it is shared by all views of that CAS.
*/
/* Design:
* Goals: Suppport PEARs, which can switch class loaders for component
* code. Different class loaders imply different instances of
* JCas cover class definitions, potentially, needing different
* generators.
*
* Keep the non-JCas path (relatively) uncluttered
*
* Concepts:
* Base-ClassLoader: there is one class loader that is considered the base.
* This is the one used for the "application code" (if it exists) driving
* the UIMA framework. It is set when the CAS or CAS Pool is created,
* according to the resource manager used. Components (annotators, flow controllers, etc.)
* run with this same class loader, unless they are contained within a PEAR;
* within PEAR components, the loader is switched (on a per-cas-view-collection basis)
* when entering the component, and switched back when the component "returns".
*
* base-generators - these are the generators associated with the Base-ClassLoader;
* there is one set per FSClassRegistry instance (and also, one per TypeSystem instance).
* cas-generators - generator set kept in the CAS svd - shared-view-data -
* that are updated as needed during the processing of the CAS, switching
* these as needed for entering / exiting PEAR components
*
* Significant data structures:
* - static map in JCasImpl - keys: typeSystem instance and classLoader instance;
* value = a hashmap of LoadedJCasType instances for that combination, used to
* make new instances of the _Type objects for views.
* - map in this class: key = class loader, value = generator-set to use for this
* class loader. This is also per unique type system, since there's one instance of
* this class per type system instance. This is used to load the cas-generators.
*
* Life cycle: Instances of this class are tied one-to-one with particular
* TypeSystem instances.
*
* At typeSystemCommit time, initGenerators is called; it loads the default
* (non-JCas) generator for all types, and then overrides the
* built-in array types with particular generators for those.
*
* *BUT* This is done only if the type system has not already been committed,
* so if the generators had (for some other CAS sharing this type system)
* already been updated to JCas generators, that is never "undone".
*
* The cas-generators are set from the base-generators (synch'd)
*
* When JCas is initialized for this CAS
* - it may have already been initialized for another CAS sharing this generator
* - it may be initializing for a non-Base-ClassLoader
*
* When JCas is initialized, if it is for the Base-ClassLoader, then the
* if this is the first time this happens for this class loader and type system,
* the base-generators are updated (synch'd) (otherwise, the base-generators have
* already been updated).
*
* The cas-generators are set from the base-generators (synch'd).
*
* If it is *not* for the Base-ClassLoader, the same thing happens, except that
* the base-generators are not updated (so they can be switched-back-to when
* leaving the PEAR component class loader environment). In this case, the
* cas-generators are set from the static map(ts, cl) in the jcasimpl.
*
* When new JCas classes are loaded due to switching to a new class loader for a PEAR,
* the static map(ts, cl) in JCasImpl is updated, and the map in this class for
* generators for this class loader is updated, and the cas-generators are loaded
* from that.
*
* When switching class loaders in component code, the cas-generators are loaded if
* a switch is needed from this class's map(cl).
*
* All refs to the base-generators are synchronized, since different CASes running on
* different threads can update these (when switching to the JCas version for the
* Base-ClassLoader).
*
*/
public class FSClassRegistry {
private static class DefaultFSGenerator implements FSGenerator {
private DefaultFSGenerator() {
super();
}
public FeatureStructure createFS(int addr, CASImpl cas) {
return new FeatureStructureImplC(cas, addr);
}
}
private TypeSystemImpl ts;
private FSGenerator[] generators;
private static final FSGenerator defaultGenerator = new DefaultFSGenerator();
/*
* Generators sometimes need to be changed while running
*
* An Annotator's process method is about to be called, but the class loader
* used for loading the JCas classes differs from the one used to load the
* Annotator class. This can happen when a PEAR with different class loader
* is inserted into a pipeline.
*
* To make this switch efficient, we keep the generators stored in a map
* keyed by the class loader.
*
* JCas creation will, after all the generators are created, call the
* saveGeneratorsForClassLoader to save a copy of the generators.
*
* Generators can be switched by calling loadGeneratorsForClassLoader
*
*/
private final Map generatorsByClassLoader = new HashMap(4);
// private final RedBlackTree rbt;
// private final TreeMap map;
private FeatureStructure[] fsArray;
FSClassRegistry(TypeSystemImpl ts) {
this.ts = ts;
}
synchronized void initGeneratorArray() {
this.generators = new FSGenerator[ts.getTypeArraySize()];
for (int i = ts.getSmallestType(); i < this.generators.length; i++) {
this.generators[i] = defaultGenerator;
}
}
/**
* adds generator for type and all its subtypes. Because of this, call this on supertypes first,
* then subtypes (otherwise subtypes will be overwritten by generators for the supertypes).
*
* @param type
* the CAS type
* @param fsFactory
* the object having a createFS method in it for this type
*/
void addClassForType(Type type, FSGenerator fsFactory) {
Iterator it = this.ts.getTypeIterator();
TypeImpl sub;
while (it.hasNext()) {
sub = (TypeImpl) it.next();
if (this.ts.subsumes(type, sub)) {
this.generators[sub.getCode()] = fsFactory;
}
}
}
/**
* No longer used, but left in for backward compatibility with older JCasgen'd
* classes
*/
public void addGeneratorForType(TypeImpl type, FSGenerator fsFactory) {
//this.generators[type.getCode()] = fsFactory;
}
// // Internal use only
// public FSGenerator getGeneratorForType(TypeImpl type) {
// return this.generators[type.getCode()];
// }
// /**
// * copies a generator for a type into another type. Called by JCas after basic types are created
// * to change the generated types for things having no JCas Java model to the most specific
// * supertype JCas Java model (if one exists). This allows writinge iterators using JCas where some
// * of the returned items may be subtypes which have no JCas cover types.
// *
// */
// public void copyGeneratorForType(TypeImpl targetType, TypeImpl sourceType) {
// this.generators[targetType.getCode()] = this.generators[sourceType.getCode()];
// }
/* only of interest when caching FSes */
void flush() {
if (this.fsArray != null) {
Arrays.fill(this.fsArray, null);
}
}
public void saveGeneratorsForClassLoader(ClassLoader cl, FSGenerator [] newGenerators) {
generatorsByClassLoader.put(cl, newGenerators);
}
public boolean swapInGeneratorsForClassLoader(ClassLoader cl, CASImpl casImpl) {
FSGenerator[] cachedGenerators = (FSGenerator[]) generatorsByClassLoader.get(cl);
if (cachedGenerators != null) {
casImpl.setLocalFsGenerators(cachedGenerators);
return true;
}
return false;
}
// // assume addr is never 0 - caller must insure this
// FeatureStructure createFSusingGenerator(int addr, CASImpl cas) {
// return this.generators[cas.getHeap().heap[addr]].createFS(addr, cas);
// }
// /*
// * Generators used are created with as much info as can be looked up once, ahead of time.
// * Things variable at run time include the cas instance, and the view.
// *
// * In this design, generators are shared with all views for a particular CAS, but are different for
// * different CAS Type Systems and Class Loaders (distinct from shared-views of the same CAS)
// *
// * Internal use only - public only to give access to JCas routines in another package
// */
// public void loadJCasGeneratorForType (int type, Constructor c, TypeImpl casType, boolean isSubtypeOfAnnotationBase) {
// FSGenerator fsGenerator = new JCasFsGenerator(type, c, isSubtypeOfAnnotationBase, ts.sofaNumFeatCode, ts.annotSofaFeatCode);
// addGeneratorForTypeInternal(casType, fsGenerator);
// }
/*
* Internal Use only
*/
public synchronized FSGenerator [] getBaseGenerators() {
return this.generators;
}
// internal use, public only for cross package ref
public synchronized void setBaseGenerators(FSGenerator [] generators) {
this.generators = generators;
}
/*
* Internal Use Only
*/
public FSGenerator [] getNewFSGeneratorSet() {
return (FSGenerator [])this.generators.clone();
}
}