blob: 74de8fb375b527f552bc6515898a7e9215eddba0 [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.fit.factory;
import static org.apache.uima.UIMAFramework.getXMLParser;
import static org.apache.uima.fit.internal.MetaDataUtil.scanDescriptors;
import static org.apache.uima.util.CasCreationUtils.mergeTypeSystems;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.WeakHashMap;
import org.apache.uima.fit.internal.ClassLoaderUtils;
import org.apache.uima.fit.internal.MetaDataType;
import org.apache.uima.fit.internal.ResourceManagerFactory;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
import org.apache.uima.resource.metadata.Import;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.resource.metadata.impl.Import_impl;
import org.apache.uima.resource.metadata.impl.TypeSystemDescription_impl;
import org.apache.uima.spi.TypeSystemDescriptionProvider;
import org.apache.uima.util.InvalidXMLException;
import org.apache.uima.util.XMLInputSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class TypeSystemDescriptionFactory {
private static final Logger LOG = LoggerFactory.getLogger(TypeSystemDescriptionFactory.class);
private static final Object SCAN_LOCK = new Object();
private static final Object CREATE_LOCK = new Object();
private static WeakHashMap<ClassLoader, String[]> typeDescriptorLocationsByClassloader;
private static WeakHashMap<ClassLoader, TypeSystemDescription> typeDescriptorByClassloader;
static {
typeDescriptorLocationsByClassloader = new WeakHashMap<>();
typeDescriptorByClassloader = new WeakHashMap<>();
}
private TypeSystemDescriptionFactory() {
// This class is not meant to be instantiated
}
/**
* Creates a TypeSystemDescription from descriptor names.
*
* @param descriptorNames
* The fully qualified, Java-style, dotted descriptor names.
* @return A TypeSystemDescription that includes the types from all of the specified files.
*/
public static TypeSystemDescription createTypeSystemDescription(String... descriptorNames) {
TypeSystemDescription typeSystem = new TypeSystemDescription_impl();
List<Import> imports = new ArrayList<>();
for (String descriptorName : descriptorNames) {
Import imp = new Import_impl();
imp.setName(descriptorName);
imports.add(imp);
}
Import[] importArray = new Import[imports.size()];
typeSystem.setImports(imports.toArray(importArray));
return typeSystem;
}
/**
* Creates a {@link TypeSystemDescription} from a descriptor file
*
* @param descriptorURIs
* The descriptor file paths.
* @return A TypeSystemDescription that includes the types from all of the specified files.
*/
public static TypeSystemDescription createTypeSystemDescriptionFromPath(
String... descriptorURIs) {
TypeSystemDescription typeSystem = new TypeSystemDescription_impl();
List<Import> imports = new ArrayList<>();
for (String descriptorURI : descriptorURIs) {
Import imp = new Import_impl();
imp.setLocation(descriptorURI);
imports.add(imp);
}
Import[] importArray = new Import[imports.size()];
typeSystem.setImports(imports.toArray(importArray));
return typeSystem;
}
/**
* Creates a {@link TypeSystemDescription} from all type descriptions that can be found via the
* default import pattern or via the {@code META-INF/org.apache.uima.fit/types.txt} files in the
* classpath.
*
* @return the auto-scanned type system.
* @throws ResourceInitializationException
* if the collected type system descriptions cannot be merged.
*/
public static TypeSystemDescription createTypeSystemDescription()
throws ResourceInitializationException {
ClassLoader cl = ClassLoaderUtils.findClassloader();
TypeSystemDescription tsd = typeDescriptorByClassloader.get(cl);
if (tsd == null) {
synchronized (CREATE_LOCK) {
List<TypeSystemDescription> tsdList = new ArrayList<>();
loadTypeSystemDescriptionsFromScannedLocations(tsdList);
loadTypeSystemDescriptionsFromSPIs(tsdList);
LOG.trace("Merging type systems and resolving imports...");
ResourceManager resMgr = ResourceManagerFactory.newResourceManager();
tsd = mergeTypeSystems(tsdList, resMgr);
typeDescriptorByClassloader.put(cl, tsd);
}
}
return (TypeSystemDescription) tsd.clone();
}
static void loadTypeSystemDescriptionsFromScannedLocations(List<TypeSystemDescription> tsdList)
throws ResourceInitializationException {
for (String location : scanTypeDescriptors()) {
try {
XMLInputSource xmlInputType1 = new XMLInputSource(location);
tsdList.add(getXMLParser().parseTypeSystemDescription(xmlInputType1));
LOG.debug("Detected type system at [{}]", location);
} catch (IOException e) {
throw new ResourceInitializationException(e);
} catch (InvalidXMLException e) {
LOG.warn("[{}] is not a type file. Ignoring.", location, e);
}
}
}
static void loadTypeSystemDescriptionsFromSPIs(List<TypeSystemDescription> tsdList) {
ServiceLoader<TypeSystemDescriptionProvider> loader = ServiceLoader
.load(TypeSystemDescriptionProvider.class);
loader.forEach(provider -> {
for (TypeSystemDescription desc : provider.listTypeSystemDescriptions()) {
tsdList.add(desc);
LOG.debug("Loaded SPI-provided type system at [{}]", desc.getSourceUrlString());
}
});
}
/**
* Get all currently accessible type system descriptor locations. A scan is actually only
* performed on the first call and the locations are cached. To force a re-scan use
* {@link #forceTypeDescriptorsScan()}.
*
* @return an array of locations.
* @throws ResourceInitializationException
* if the locations could not be resolved.
*/
public static String[] scanTypeDescriptors() throws ResourceInitializationException {
synchronized (SCAN_LOCK) {
ClassLoader cl = ClassLoaderUtils.findClassloader();
String[] typeDescriptorLocations = typeDescriptorLocationsByClassloader.get(cl);
if (typeDescriptorLocations == null) {
typeDescriptorLocations = scanDescriptors(MetaDataType.TYPE_SYSTEM);
typeDescriptorLocationsByClassloader.put(cl, typeDescriptorLocations);
}
return typeDescriptorLocations;
}
}
/**
* Force rescan of type descriptors. The next call to {@link #scanTypeDescriptors()} will rescan
* all auto-import locations.
*/
public static void forceTypeDescriptorsScan() {
synchronized (SCAN_LOCK) {
typeDescriptorLocationsByClassloader.clear();
typeDescriptorByClassloader.clear();
}
}
}