| /* |
| * 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.Collections; |
| 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 |
| * |
| */ |
| // This map can be accessed on different threads at the same time |
| private final Map<ClassLoader, FSGenerator[]> generatorsByClassLoader = |
| Collections.synchronizedMap(new HashMap<ClassLoader, FSGenerator[]>(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 |
| */ |
| synchronized void addClassForType(Type type, FSGenerator fsFactory) { |
| Iterator<Type> 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 |
| * All callers must by synchronized |
| * although may not be strictly necessary if you can prove that |
| * no updates to the generators array could be occuring in |
| * another thread |
| */ |
| |
| 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 synchronized FSGenerator [] getNewFSGeneratorSet() { |
| return (FSGenerator [])this.generators.clone(); |
| } |
| } |