blob: d492f578dca381b46600d94d177763c4b71cbf1d [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 java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static org.apache.uima.UIMAFramework.produceResource;
import static org.apache.uima.fit.factory.ConfigurationParameterFactory.canParameterBeSet;
import static org.apache.uima.fit.factory.ConfigurationParameterFactory.createConfigurationData;
import java.io.File;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.uima.UIMAFramework;
import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.collection.CollectionReaderDescription;
import org.apache.uima.fit.descriptor.ExternalResource;
import org.apache.uima.fit.factory.ConfigurationParameterFactory.ConfigurationData;
import org.apache.uima.fit.internal.ExtendedExternalResourceDescription_impl;
import org.apache.uima.fit.internal.ReflectionUtil;
import org.apache.uima.fit.internal.ResourceList;
import org.apache.uima.resource.ConfigurableDataResourceSpecifier;
import org.apache.uima.resource.CustomResourceSpecifier;
import org.apache.uima.resource.DataResource;
import org.apache.uima.resource.ExternalResourceDependency;
import org.apache.uima.resource.ExternalResourceDescription;
import org.apache.uima.resource.FileResourceSpecifier;
import org.apache.uima.resource.Parameter;
import org.apache.uima.resource.ParameterizedDataResource;
import org.apache.uima.resource.Resource;
import org.apache.uima.resource.ResourceCreationSpecifier;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.SharedResourceObject;
import org.apache.uima.resource.impl.ConfigurableDataResourceSpecifier_impl;
import org.apache.uima.resource.impl.ConfigurableDataResource_impl;
import org.apache.uima.resource.impl.ExternalResourceDependency_impl;
import org.apache.uima.resource.impl.ExternalResourceDescription_impl;
import org.apache.uima.resource.impl.FileResourceSpecifier_impl;
import org.apache.uima.resource.impl.Parameter_impl;
import org.apache.uima.resource.metadata.ExternalResourceBinding;
import org.apache.uima.resource.metadata.ResourceManagerConfiguration;
import org.apache.uima.resource.metadata.impl.ExternalResourceBinding_impl;
import org.apache.uima.resource.metadata.impl.ResourceManagerConfiguration_impl;
import org.apache.uima.resource.metadata.impl.ResourceMetaData_impl;
import org.apache.uima.util.InvalidXMLException;
/**
* Helper methods for external resources.
*
*/
public final class ExternalResourceFactory {
public static final String PARAM_RESOURCE_NAME = "__UIMAFIT_RESOURCE_NAME__";
/**
* Used to separate resource name from key for nested resource.
*/
public static final String PREFIX_SEPARATOR = "##";
/**
* Counter used to create unique resource names.
*/
private final static AtomicLong DISAMBIGUATOR = new AtomicLong();
private ExternalResourceFactory() {
// This class is not meant to be instantiated
}
/**
* This method determines if the field is annotated with
* {@link org.apache.uima.fit.descriptor.ExternalResource}.
*
* @param field
* the field to analyze
* @return whether the field is marked as an external resource
*/
public static boolean isExternalResourceField(Field field) {
return ReflectionUtil.isAnnotationPresent(field,
org.apache.uima.fit.descriptor.ExternalResource.class);
}
/**
* Create an external resource description for a custom resource.
*
* @param aInterface
* the interface the resource should implement.
* @param aParams
* parameters passed to the resource when it is created.
* @return the description.
* @see CustomResourceSpecifier
*/
public static ExternalResourceDescription createResourceDescription(
Class<? extends Resource> aInterface, Object... aParams) {
return createNamedResourceDescription(uniqueResourceKey(aInterface.getName()), aInterface,
aParams);
}
/**
* Create an external resource description for a custom resource.
*
* @param aName
* the name of the resource (the key).
* @param aInterface
* the interface the resource should implement.
* @param aParams
* parameters passed to the resource when it is created.
* @return the description.
* @see CustomResourceSpecifier
*/
public static ExternalResourceDescription createNamedResourceDescription(final String aName,
Class<? extends Resource> aInterface, Object... aParams) {
ConfigurationParameterFactory.ensureParametersComeInPairs(aParams);
// Extract ExternalResourceDescriptions from configurationData
List<ExternalResourceBinding> bindings = new ArrayList<ExternalResourceBinding>();
List<ExternalResourceDescription> descs = new ArrayList<ExternalResourceDescription>();
for (Entry<String, ExternalResourceDescription> res : extractResourceParameters(aParams)
.entrySet()) {
bindings.add(createResourceBinding(res.getKey(), res.getValue()));
descs.add(res.getValue());
}
ResourceSpecifier spec;
if (ConfigurableDataResource_impl.class.isAssignableFrom(aInterface)) {
ConfigurationData cfg = ConfigurationParameterFactory.createConfigurationData(aParams);
ResourceMetaData_impl meta = new ResourceMetaData_impl();
ConfigurationData reflectedConfigurationData = createConfigurationData(aInterface);
ResourceCreationSpecifierFactory.setConfigurationParameters(meta,
reflectedConfigurationData.configurationParameters,
reflectedConfigurationData.configurationValues);
ResourceCreationSpecifierFactory.setConfigurationParameters(meta,
cfg.configurationParameters, cfg.configurationValues);
ConfigurableDataResourceSpecifier_impl spec1 = new ConfigurableDataResourceSpecifier_impl();
spec1.setUrl("");
spec1.setMetaData(meta);
spec = spec1;
} else {
List<Parameter> params = new ArrayList<Parameter>();
if (aParams != null) {
for (int i = 0; i < aParams.length / 2; i++) {
if (ExternalResourceFactory.getResourceParameterType(aParams[i * 2 + 1]) != ResourceValueType.NO_RESOURCE) {
continue;
}
Parameter param = new Parameter_impl();
param.setName((String) aParams[i * 2]);
param.setValue((String) aParams[i * 2 + 1]);
params.add(param);
}
}
CustomResourceSpecifier spec1 = UIMAFramework.getResourceSpecifierFactory()
.createCustomResourceSpecifier();
spec1.setResourceClassName(aInterface.getName());
spec1.setParameters(params.toArray(new Parameter[params.size()]));
spec = spec1;
}
ExtendedExternalResourceDescription_impl extRes = new ExtendedExternalResourceDescription_impl();
extRes.setName(aName);
extRes.setResourceSpecifier(spec);
extRes.setExternalResourceBindings(bindings);
extRes.setExternalResources(descs);
return extRes;
}
/**
* Create an external resource description for a {@link SharedResourceObject}.
* @param aUrl
* the URL from which the resource is initialized.
* @param aInterface
* the interface the resource should implement.
* @param aParams
* parameters passed to the resource when it is created.
*
* @return the description.
* @see ConfigurableDataResourceSpecifier
* @see SharedResourceObject
*/
public static ExternalResourceDescription createSharedResourceDescription(
String aUrl, Class<? extends SharedResourceObject> aInterface, Object... aParams) {
return createNamedResourceDescriptionUsingUrl(uniqueResourceKey(aInterface.getName()), aInterface,
aUrl, aParams);
}
/**
* Create an external resource description for a {@link SharedResourceObject}.
* @param aUrl
* the URL from which the resource is initialized.
* @param aInterface
* the interface the resource should implement.
* @param aParams
* parameters passed to the resource when it is created.
*
* @return the description.
* @see ConfigurableDataResourceSpecifier
* @see SharedResourceObject
*/
public static ExternalResourceDescription createSharedResourceDescription(
URL aUrl, Class<? extends SharedResourceObject> aInterface, Object... aParams) {
return createNamedResourceDescriptionUsingUrl(uniqueResourceKey(aInterface.getName()), aInterface,
aUrl.toString(), aParams);
}
/**
* Create an external resource description for a {@link SharedResourceObject}.
* @param aFile
* the file from which the resource is initialized.
* @param aInterface
* the interface the resource should implement.
* @param aParams
* parameters passed to the resource when it is created.
*
* @return the description.
* @see ConfigurableDataResourceSpecifier
* @see SharedResourceObject
*/
public static ExternalResourceDescription createSharedResourceDescription(
File aFile, Class<? extends SharedResourceObject> aInterface, Object... aParams) {
try {
return createSharedResourceDescription(aFile.toURI().toURL(), aInterface, aParams);
} catch (MalformedURLException e) {
// This is something that usually cannot happen, so we degrade this to an
// IllegalArgumentException which is a RuntimeException that does not need to be caught.
throw new IllegalArgumentException("File converts to illegal URL [" + aFile + "]");
}
}
/**
* Create an external resource description for a {@link SharedResourceObject}.
*
* @param aName
* the name of the resource (the key).
* @param aInterface
* the interface the resource should implement.
* @param aUrl
* the URL from which the resource is initialized.
* @param aParams
* parameters passed to the resource when it is created.
* @return the description.
* @see ConfigurableDataResourceSpecifier
* @see SharedResourceObject
*/
public static ExternalResourceDescription createNamedResourceDescriptionUsingUrl(final String aName,
Class<? extends SharedResourceObject> aInterface, String aUrl, Object... aParams) {
// Extract ExternalResourceDescriptions from configurationData
List<ExternalResourceBinding> bindings = new ArrayList<ExternalResourceBinding>();
List<ExternalResourceDescription> descs = new ArrayList<ExternalResourceDescription>();
for (Entry<String, ExternalResourceDescription> res : extractResourceParameters(aParams)
.entrySet()) {
bindings.add(createResourceBinding(res.getKey(), res.getValue()));
descs.add(res.getValue());
}
ConfigurationData cfg = ConfigurationParameterFactory.createConfigurationData(aParams);
ResourceMetaData_impl meta = new ResourceMetaData_impl();
ConfigurationData reflectedConfigurationData = createConfigurationData(aInterface);
ResourceCreationSpecifierFactory.setConfigurationParameters(meta,
reflectedConfigurationData.configurationParameters,
reflectedConfigurationData.configurationValues);
ResourceCreationSpecifierFactory.setConfigurationParameters(meta, cfg.configurationParameters,
cfg.configurationValues);
ConfigurableDataResourceSpecifier_impl spec = new ConfigurableDataResourceSpecifier_impl();
spec.setUrl(aUrl);
spec.setMetaData(meta);
ExtendedExternalResourceDescription_impl extRes = new ExtendedExternalResourceDescription_impl();
extRes.setName(aName);
extRes.setResourceSpecifier(spec);
extRes.setImplementationName(aInterface.getName());
extRes.setExternalResourceBindings(bindings);
extRes.setExternalResources(descs);
return extRes;
}
/**
* Create an external resource description for a file addressable via an URL.
*
* @param aName
* the name of the resource (the key).
* @param aUrl
* a URL.
* @return the description.
* @see FileResourceSpecifier
*/
public static ExternalResourceDescription createNamedFileResourceDescription(
final String aName, String aUrl) {
ExternalResourceDescription extRes = new ExternalResourceDescription_impl();
extRes.setName(aName);
FileResourceSpecifier frs = new FileResourceSpecifier_impl();
frs.setFileUrl(aUrl);
extRes.setResourceSpecifier(frs);
return extRes;
}
/**
* Create an external resource binding.
*
* @param aKey
* the key to bind to.
* @param aResource
* the resource to bind.
* @return the description.
*/
public static ExternalResourceBinding createResourceBinding(final String aKey,
final ExternalResourceDescription aResource) {
return createResourceBinding(aKey, aResource.getName());
}
/**
* Create an external resource binding.
*
* @param aKey
* the key to bind to.
* @param aResourceKey
* the resource key to bind.
* @return the description.
*/
public static ExternalResourceBinding createResourceBinding(final String aKey,
final String aResourceKey) {
ExternalResourceBinding extResBind = new ExternalResourceBinding_impl();
extResBind.setResourceName(aResourceKey);
extResBind.setKey(aKey);
return extResBind;
}
/**
* Creates an ExternalResourceDependency for a field annotated with
* {@link org.apache.uima.fit.descriptor.ExternalResource}.
*
* @param field
* the field to analyze
* @return a external resource dependency
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static ExternalResourceDependency createResourceDependency(Field field) {
ExternalResource era = ReflectionUtil.getAnnotation(field, ExternalResource.class);
// Get the binding key for the specified field. If no key is set, use the field name as key.
String key = era.key();
if (key.length() == 0) {
key = field.getName();
}
// Get the type of class/interface a resource has to implement to bind to the annotated field.
// If no API is set, get it from the annotated field type.
Class<? extends Resource> api = era.api();
// If no api is specified, look at the annotated field
if (api == Resource.class) {
if (Resource.class.isAssignableFrom(field.getType())
|| SharedResourceObject.class.isAssignableFrom(field.getType())) {
// If no API is set, check if the field type is already a resource type
api = (Class<? extends Resource>) field.getType();
} else {
// If the field does not have a resource type, assume whatever. This allows to use
// a resource locator without having to specify the api parameter. It also allows
// to directly inject Java objects - yes, I know that Object does not extend
// Resource - REC, 2011-03-25
api = (Class) Object.class;
}
}
return ExternalResourceFactory.createResourceDependency(key, api, !era.mandatory(),
era.description());
}
/**
* Creates an ExternalResourceDependency for a given key and interface
*
* @param aKey
* the resource key
* @param aInterface
* the resource interface
* @param aOptional
* determines whether the dependency is optional
* @param aDescription
* a description of the resource
* @return the external resource dependency
*/
public static ExternalResourceDependency createResourceDependency(final String aKey,
final Class<?> aInterface, final boolean aOptional, String aDescription) {
ExternalResourceDependency dep = new ExternalResourceDependency_impl();
dep.setInterfaceName(aInterface.getName());
dep.setKey(aKey);
dep.setOptional(aOptional);
dep.setDescription(aDescription);
return dep;
}
/**
* @param cls
* the class to analyze
* @return the external resource dependencies
* @throws ResourceInitializationException
* if the external resource dependencies could not be created
*/
public static ExternalResourceDependency[] createResourceDependencies(
Class<?> cls) throws ResourceInitializationException {
Map<String, ExternalResourceDependency> depMap = new HashMap<String, ExternalResourceDependency>();
ExternalResourceFactory.createResourceDependencies(cls, cls, depMap);
Collection<ExternalResourceDependency> deps = depMap.values();
return deps.toArray(new ExternalResourceDependency[deps.size()]);
}
private static <T> void createResourceDependencies(Class<?> baseCls, Class<?> cls,
Map<String, ExternalResourceDependency> dependencies)
throws ResourceInitializationException {
if (cls.getSuperclass() != null) {
createResourceDependencies(baseCls, cls.getSuperclass(), dependencies);
}
for (Field field : cls.getDeclaredFields()) {
if (!ReflectionUtil.isAnnotationPresent(field, ExternalResource.class)) {
continue;
}
ExternalResourceDependency dep = createResourceDependency(field);
if (dependencies.containsKey(dep.getKey())) {
throw new ResourceInitializationException(new IllegalStateException("Key [" + dep.getKey()
+ "] may only be used on a single field."));
}
dependencies.put(dep.getKey(), dep);
}
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* is encounter that has the specified key, the resource will be bound.
* <p>
* <b>Caveat</b>: If you use this method, you may expect that {@link DataResource#getUrl()} or
* {@link DataResource#getUri()} will return the same URL that you have specified here. This may
* <b>NOT</b> be the case. UIMA will internally try to resolve the URL via a
* {@link ResourceManager}. If it cannot resolve a remove URL, this mechanism will think it may be
* a local file and will return some local path - or it may redirect it to some location as though
* fit by the {@link ResourceManager}.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aUrl
* a URL.
* @throws InvalidXMLException
* if import resolution failed
* @see FileResourceSpecifier
*/
public static void bindResource(ResourceSpecifier aDesc, String aKey, URL aUrl)
throws InvalidXMLException {
bindResource(aDesc, aKey, aUrl.toString());
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* is encounter that has the specified key, the resource will be bound.
* <p>
* <b>Caveat</b>: If you use this method, you may expect that {@link DataResource#getUrl()} or
* {@link DataResource#getUri()} will return the URL of the file that you have specified here.
* This may <b>NOT</b> be the case. UIMA will internally try to resolve the URL via a
* {@link ResourceManager}. If it cannot resolve a remove URL, this mechanism will think it may be
* a local file and will return some local path - or it may redirect it to some location as though
* fit by the {@link ResourceManager}.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aFile
* a file.
* @throws InvalidXMLException
* if import resolution failed
* @see FileResourceSpecifier
*/
public static void bindResource(ResourceSpecifier aDesc, String aKey, File aFile)
throws InvalidXMLException {
try {
bindResource(aDesc, aKey, aFile.toURI().toURL());
} catch (MalformedURLException e) {
// This is something that usually cannot happen, so we degrade this to an
// IllegalArgumentException which is a RuntimeException that does not need to be caught.
throw new IllegalArgumentException("File converts to illegal URL [" + aFile + "]");
}
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* is encounter that has the specified key, the resource will be bound.
* <p>
* <b>Caveat</b>: If you use this method, you may expect that {@link DataResource#getUrl()} or
* {@link DataResource#getUri()} will return the same URL that you have specified here. This is
* may <b>NOT</b> be the case. UIMA will internally try to resolve the URL via a
* {@link ResourceManager}. If it cannot resolve a remove URL, this mechanism will think it may be
* a local file and will return some local path - or it may redirect it to some location as though
* fit by the {@link ResourceManager}.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aUrl
* a URL.
* @throws InvalidXMLException
* if import resolution failed
* @see FileResourceSpecifier
*/
public static void bindResource(ResourceSpecifier aDesc, String aKey, String aUrl)
throws InvalidXMLException {
ExternalResourceDescription extRes = createNamedFileResourceDescription(aKey, aUrl);
bindResource(aDesc, aKey, extRes);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with a compatible type is found, the resource will be bound.
*
* @param aDesc
* a description.
* @param aRes
* the resource to bind.
* @param aParams
* parameters passed to the resource when it is created.
* @throws InvalidXMLException
* if import resolution failed
* @throws ClassNotFoundException
* if the resource implementation class or interface class could not be accessed
* @see CustomResourceSpecifier
*/
public static void bindResource(ResourceSpecifier aDesc, Class<? extends Resource> aRes,
String... aParams) throws InvalidXMLException, ClassNotFoundException {
bindResource(aDesc, aRes, aRes, aParams);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with a compatible type is found, the resource will be bound.
*
* @param aDesc
* a description.
* @param aApi
* the resource interface.
* @param aRes
* the resource to bind.
* @param aParams
* parameters passed to the resource when it is created.
* @throws InvalidXMLException
* if import resolution failed
* @throws ClassNotFoundException
* if the resource implementation class or interface class could not be accessed
* @see CustomResourceSpecifier
*/
public static void bindResource(ResourceSpecifier aDesc, Class<?> aApi,
Class<? extends Resource> aRes, String... aParams) throws InvalidXMLException,
ClassNotFoundException {
// Appending a disambiguation suffix it possible to have multiple instances of the same
// resource with different settings to different keys.
ExternalResourceDescription extRes = createNamedResourceDescription(
uniqueResourceKey(aRes.getName()), aRes, (Object[]) aParams);
bindResource(aDesc, extRes);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with a compatible type is found, the resource will be bound.
* @param aDesc
* a description.
* @param aRes
* the resource to bind.
* @param aUrl
* the URL from which the resource is initialized.
* @param aParams
* parameters passed to the resource when it is created.
* @throws InvalidXMLException
* if import resolution failed
* @throws ClassNotFoundException
* if the resource implementation class or interface class could not be accessed
* @see SharedResourceObject
*/
public static void bindResourceUsingUrl(ResourceSpecifier aDesc,
Class<? extends SharedResourceObject> aRes, String aUrl, Object... aParams)
throws InvalidXMLException, ClassNotFoundException {
ExternalResourceDescription extRes = createNamedResourceDescriptionUsingUrl(
uniqueResourceKey(aRes.getName()), aRes, aUrl, aParams);
bind((AnalysisEngineDescription) aDesc, extRes);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* is encountered that has a key equal to the API class name, the resource will be bound.
*
* @param aDesc
* a description.
* @param aApi
* the resource interface.
* @param aRes
* the resource to bind.
* @param aUrl
* the URL from which the resource is initialized.
* @param aParams
* parameters passed to the resource when it is created.
* @throws InvalidXMLException
* if import resolution failed
* @see SharedResourceObject
*/
public static void bindResourceUsingUrl(ResourceSpecifier aDesc, Class<?> aApi,
Class<? extends SharedResourceObject> aRes, String aUrl, Object... aParams)
throws InvalidXMLException {
bindResourceUsingUrl(aDesc, aApi.getName(), aRes, aUrl, aParams);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with the given key is encountered the resource will be bound.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aRes
* the resource to bind.
* @param aUrl
* the URL from which the resource is initialized.
* @param aParams
* parameters passed to the resource when it is created.
* @throws InvalidXMLException
* if import resolution failed
* @see SharedResourceObject
*/
public static void bindResourceUsingUrl(ResourceSpecifier aDesc, String aKey,
Class<? extends SharedResourceObject> aRes, String aUrl, Object... aParams)
throws InvalidXMLException {
ExternalResourceDescription extRes = createNamedResourceDescriptionUsingUrl(
uniqueResourceKey(aRes.getName()), aRes, aUrl, aParams);
bind((AnalysisEngineDescription) aDesc, aKey, extRes);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with the given key is encountered, the given resource is bound to it.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aRes
* the resource to bind.
* @param aParams
* parameters passed to the resource when it is created.
* @throws InvalidXMLException
* if import resolution failed
* @see CustomResourceSpecifier
*/
public static void bindResource(ResourceSpecifier aDesc, String aKey,
Class<? extends Resource> aRes, String... aParams) throws InvalidXMLException {
if (ParameterizedDataResource.class.isAssignableFrom(aRes)) {
createDependency(aDesc, aKey, DataResource.class);
}
// Appending a disambiguation suffix it possible to have multiple instances of the same
// resource with different settings to different keys.
ExternalResourceDescription extRes = createNamedResourceDescription(
uniqueResourceKey(aRes.getName()), aRes, (Object[]) aParams);
bindResource(aDesc, aKey, extRes);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with a compatible type is found, the given resource is bound to it.
*
* @param aDesc
* a description.
* @param aResDesc
* the resource description.
* @throws InvalidXMLException
* if import resolution failed
* @throws ClassNotFoundException
* if the resource implementation class or interface class could not be accessed
*/
public static void bindResource(ResourceSpecifier aDesc, ExternalResourceDescription aResDesc)
throws InvalidXMLException, ClassNotFoundException {
// Dispatch
if (aDesc instanceof AnalysisEngineDescription) {
bind((AnalysisEngineDescription) aDesc, aResDesc);
}
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with the given key is encountered, the given resource is bound to it.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aResDesc
* the resource description.
* @throws InvalidXMLException
* if import resolution failed
*/
public static void bindResource(ResourceSpecifier aDesc, String aKey,
ExternalResourceDescription aResDesc) throws InvalidXMLException {
// Dispatch
if (aDesc instanceof AnalysisEngineDescription) {
bind((AnalysisEngineDescription) aDesc, aKey, aResDesc);
}
}
/**
* Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
* components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
* resource dependencies can be automatically generated by uimaFIT for such components.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aImpl
* the resource implementation.
* @param aParams
* additional parameters supported by the resource.
* @throws InvalidXMLException
* if import resolution failed
*/
public static void createDependencyAndBind(ResourceSpecifier aDesc, String aKey,
Class<? extends Resource> aImpl, String... aParams) throws InvalidXMLException {
Class<?> api = (ParameterizedDataResource.class.isAssignableFrom(aImpl)) ? DataResource.class
: aImpl;
createDependencyAndBind(aDesc, aKey, aImpl, api, aParams);
}
/**
* Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
* components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
* resource dependencies can be automatically generated by uimaFIT for such components.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aImpl
* the resource implementation.
* @param aApi
* the resource interface
* @param aParams
* additional parameters supported by the resource.
* @throws InvalidXMLException
* if import resolution failed
*/
public static void createDependencyAndBind(ResourceSpecifier aDesc, String aKey,
Class<? extends Resource> aImpl, Class<?> aApi, String... aParams)
throws InvalidXMLException {
createDependency(aDesc, aKey, aApi);
bindResource(aDesc, aKey, aImpl, aParams);
}
/**
* Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
* components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
* resource dependencies can be automatically generated by uimaFIT for such components.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aApi
* the resource API.
*/
public static void createDependency(ResourceSpecifier aDesc, String aKey, Class<?> aApi) {
ExternalResourceDependency[] deps = getResourceDependencies(aDesc);
if (deps == null) {
deps = new ExternalResourceDependency[] {};
}
// Check if the resource dependency is already present
boolean found = false;
for (ExternalResourceDependency dep : deps) {
if (dep.getKey().equals(aKey)) {
found = true;
break;
}
}
// If not, create one
if (!found) {
setResourceDependencies(
aDesc,
ArrayUtils.add(deps,
createResourceDependency(aKey, aApi, false, null)));
}
}
/**
* Convenience method to set the external resource dependencies on a resource specifier.
* Unfortunately different methods need to be used for different sub-classes.
*
* @throws IllegalArgumentException
* if the sub-class passed is not supported.
*/
private static void setResourceDependencies(ResourceSpecifier aDesc,
ExternalResourceDependency[] aDependencies) {
if (aDesc instanceof CollectionReaderDescription) {
((CollectionReaderDescription) aDesc).setExternalResourceDependencies(aDependencies);
} else if (aDesc instanceof AnalysisEngineDescription) {
((AnalysisEngineDescription) aDesc).setExternalResourceDependencies(aDependencies);
} else {
throw new IllegalArgumentException(
"Resource specified cannot have external resource dependencies");
}
}
/**
* Convenience method to get the external resource dependencies from a resource specifier.
* Unfortunately different methods need to be used for different sub-classes.
*
* @throws IllegalArgumentException
* if the sub-class passed is not supported.
*/
private static ExternalResourceDependency[] getResourceDependencies(
ResourceSpecifier aDesc) {
if (aDesc instanceof CollectionReaderDescription) {
return ((CollectionReaderDescription) aDesc).getExternalResourceDependencies();
} else if (aDesc instanceof AnalysisEngineDescription) {
return ((AnalysisEngineDescription) aDesc).getExternalResourceDependencies();
} else {
throw new IllegalArgumentException(
"Resource specified cannot have external resource dependencies");
}
}
/**
* Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
* components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
* resource dependencies can be automatically generated by uimaFIT for such components.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aImpl
* the resource implementation.
* @param aUrl
* the resource URL.
* @param aParams
* additional parameters supported by the resource.
* @throws InvalidXMLException
* if import resolution failed
*/
public static void createDependencyAndBindUsingUrl(AnalysisEngineDescription aDesc, String aKey,
Class<? extends SharedResourceObject> aImpl, String aUrl, Object... aParams)
throws InvalidXMLException {
if (aDesc.getExternalResourceDependency(aKey) == null) {
ExternalResourceDependency[] deps = aDesc.getExternalResourceDependencies();
if (deps == null) {
deps = new ExternalResourceDependency[] {};
}
aDesc.setExternalResourceDependencies(ArrayUtils.add(deps,
createResourceDependency(aKey, aImpl, false, null)));
}
bindResourceUsingUrl(aDesc, aKey, aImpl, aUrl, aParams);
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* a compatible type is found, the given resource is bound to it.
*
* @param aDesc
* a description.
* @param aResDesc
* the resource description.
*/
private static void bind(AnalysisEngineDescription aDesc, ExternalResourceDescription aResDesc)
throws InvalidXMLException, ClassNotFoundException {
// Recursively address delegates
if (!aDesc.isPrimitive()) {
for (Object delegate : aDesc.getDelegateAnalysisEngineSpecifiers().values()) {
bindResource((ResourceSpecifier) delegate, aResDesc);
}
}
// Bind if necessary
Class<?> resClass = Class.forName(getImplementationName(aResDesc));
for (ExternalResourceDependency dep : aDesc.getExternalResourceDependencies()) {
Class<?> apiClass = Class.forName(dep.getInterfaceName());
// Never bind fields of type Object. See also ExternalResourceInitializer#getApi()
if (apiClass.equals(Object.class)) {
continue;
}
if (apiClass.isAssignableFrom(resClass)) {
bindResource(aDesc, dep.getKey(), aResDesc);
}
}
}
/**
* Scan the given resource specifier for external resource dependencies and whenever a dependency
* with the given key is encountered, the given resource is bound to it.
*
* @param aDesc
* a description.
* @param aKey
* the key to bind to.
* @param aResDesc
* the resource description.
*/
private static void bind(AnalysisEngineDescription aDesc, String aKey,
ExternalResourceDescription aResDesc) throws InvalidXMLException {
// Recursively address delegates
if (!aDesc.isPrimitive()) {
for (Object delegate : aDesc.getDelegateAnalysisEngineSpecifiers().values()) {
bindResource((ResourceSpecifier) delegate, aKey, aResDesc);
}
}
// Bind if necessary
for (ExternalResourceDependency dep : aDesc.getExternalResourceDependencies()) {
if (aKey.equals(dep.getKey())) {
bindResource(aDesc, aKey, aResDesc);
}
}
}
/**
* Create a new external resource binding.
*
* @param aResMgrCfg
* the resource manager to create the binding in.
* @param aBindTo
* what key to bind to.
* @param aRes
* the resource that should be bound.
*/
public static void bindResource(ResourceManagerConfiguration aResMgrCfg, String aBindTo,
ExternalResourceDescription aRes) {
// Create a map of all bindings
Map<String, ExternalResourceBinding> bindings = new HashMap<String, ExternalResourceBinding>();
for (ExternalResourceBinding b : aResMgrCfg.getExternalResourceBindings()) {
bindings.put(b.getKey(), b);
}
// Create a map of all resources
Map<String, ExternalResourceDescription> resources = new HashMap<String, ExternalResourceDescription>();
for (ExternalResourceDescription r : aResMgrCfg.getExternalResources()) {
resources.put(r.getName(), r);
}
// For the current resource, add resource and binding
ExternalResourceBinding extResBind = createResourceBinding(aBindTo, aRes);
bindings.put(extResBind.getKey(), extResBind);
resources.put(aRes.getName(), aRes);
// Handle nested resources
bindNestedResources(aRes, bindings, resources);
// Commit everything to the resource manager configuration
aResMgrCfg.setExternalResourceBindings(bindings.values().toArray(
new ExternalResourceBinding[bindings.size()]));
aResMgrCfg.setExternalResources(resources.values().toArray(
new ExternalResourceDescription[resources.size()]));
}
/**
* Create a new external resource binding.
*
* @param aRes
* the resource to bind to
* @param aBindTo
* what key to bind to.
* @param aNestedRes
* the resource that should be bound.
*/
public static void bindResource(ExternalResourceDescription aRes, String aBindTo,
ExternalResourceDescription aNestedRes) {
if (!(aRes instanceof ExtendedExternalResourceDescription_impl)) {
throw new IllegalArgumentException("Nested resources are only supported on instances of ["
+ ExtendedExternalResourceDescription_impl.class.getName() + "] which"
+ "can be created with uimaFIT's createExternalResourceDescription() methods.");
}
ExtendedExternalResourceDescription_impl extRes = (ExtendedExternalResourceDescription_impl) aRes;
// Create a map of all bindings
Map<String, ExternalResourceBinding> bindings = new HashMap<String, ExternalResourceBinding>();
for (ExternalResourceBinding b : extRes.getExternalResourceBindings()) {
bindings.put(b.getKey(), b);
}
// Create a map of all resources
Map<String, ExternalResourceDescription> resources = new HashMap<String, ExternalResourceDescription>();
for (ExternalResourceDescription r : extRes.getExternalResources()) {
resources.put(r.getName(), r);
}
// For the current resource, add resource and binding
ExternalResourceBinding extResBind = createResourceBinding(aBindTo, aNestedRes);
bindings.put(extResBind.getKey(), extResBind);
resources.put(aNestedRes.getName(), aNestedRes);
// Handle nested resources
bindNestedResources(aRes, bindings, resources);
// Commit everything to the resource manager configuration
extRes.setExternalResourceBindings(bindings.values());
extRes.setExternalResources(resources.values());
}
/**
* Helper method to recursively bind resources bound to resources.
*
* @param aRes
* resource.
* @param aBindings
* bindings already made.
* @param aResources
* resources already bound.
*/
private static void bindNestedResources(ExternalResourceDescription aRes,
Map<String, ExternalResourceBinding> aBindings,
Map<String, ExternalResourceDescription> aResources) {
// Handle nested resources
if (aRes instanceof ExtendedExternalResourceDescription_impl) {
ExtendedExternalResourceDescription_impl extRes = (ExtendedExternalResourceDescription_impl) aRes;
// Tell the external resource its name. This is needed in order to find the resources
// bound to this resource later on. Set only if the resource supports this parameter.
// Mind that supporting this parameter is mandatory for resource implementing
// ExternalResourceAware.
if (canParameterBeSet(extRes.getResourceSpecifier(), PARAM_RESOURCE_NAME)) {
ConfigurationParameterFactory.setParameter(extRes.getResourceSpecifier(),
PARAM_RESOURCE_NAME, aRes.getName());
}
// Create a map of all resources
Map<String, ExternalResourceDescription> res = new HashMap<String, ExternalResourceDescription>();
for (ExternalResourceDescription r : extRes.getExternalResources()) {
res.put(r.getName(), r);
}
// Bind nested resources
for (ExternalResourceBinding b : extRes.getExternalResourceBindings()) {
// Avoid re-prefixing the resource name
String key = b.getKey();
if (!key.startsWith(aRes.getName() + PREFIX_SEPARATOR)) {
key = aRes.getName() + PREFIX_SEPARATOR + b.getKey();
}
// Avoid unnecessary binding and an infinite loop when a resource binds to itself
if (!aBindings.containsKey(key)) {
// Mark the current binding as processed so we do not recurse
aBindings.put(key, b);
ExternalResourceDescription nestedRes = res.get(b.getResourceName());
aResources.put(nestedRes.getName(), nestedRes);
bindNestedResources(nestedRes, aBindings, aResources);
// Set the proper key on the binding.
b.setKey(key);
}
}
}
}
/**
* Create a new external resource binding.
*
* @param aDesc
* the specifier to create the binding in.
* @param aBindTo
* what key to bind to.
* @param aRes
* the resource that should be bound.
*/
public static void bindResource(ResourceCreationSpecifier aDesc, String aBindTo,
ExternalResourceDescription aRes) {
ResourceManagerConfiguration resMgrCfg = aDesc.getResourceManagerConfiguration();
if (resMgrCfg == null) {
resMgrCfg = new ResourceManagerConfiguration_impl();
aDesc.setResourceManagerConfiguration(resMgrCfg);
}
bindResource(resMgrCfg, aBindTo, aRes);
}
/**
* Create a new external resource binding.
*
* @param aResMgrCfg
* the resource manager to create the binding in.
* @param aBindTo
* what key to bind to.
* @param aRes
* the resource that should be bound.
*/
public static void bindResource(ResourceManagerConfiguration aResMgrCfg, String aBindTo,
String aRes) {
ExternalResourceBinding extResBind = createResourceBinding(aBindTo, aRes);
aResMgrCfg.addExternalResourceBinding(extResBind);
}
/**
* Create a new external resource binding.
*
* @param aDesc
* the specifier to create the binding in.
* @param aBindTo
* what key to bind to.
* @param aRes
* the resource that should be bound.
*/
public static void bindResource(ResourceCreationSpecifier aDesc, String aBindTo, String aRes) {
ResourceManagerConfiguration resMgrCfg = aDesc.getResourceManagerConfiguration();
if (resMgrCfg == null) {
resMgrCfg = new ResourceManagerConfiguration_impl();
aDesc.setResourceManagerConfiguration(resMgrCfg);
}
bindResource(resMgrCfg, aBindTo, aRes);
}
static String uniqueResourceKey(String aKey) {
return aKey + '-' + DISAMBIGUATOR.getAndIncrement();
}
/**
* Find the name of the class implementing this resource. The location where this name is stored
* varies, depending if the resource extends {@link SharedResourceObject} or implements
* {@link Resource}.
*
* @param aDesc
* the external resource description.
* @return the implementation name.
*/
protected static String getImplementationName(ExternalResourceDescription aDesc) {
if (aDesc.getResourceSpecifier() instanceof CustomResourceSpecifier) {
return ((CustomResourceSpecifier) aDesc.getResourceSpecifier()).getResourceClassName();
} else {
return aDesc.getImplementationName();
}
}
/**
* Extracts the external resource from the configuration parameters and nulls out these
* parameters. Mind that the array passed to this method is modified by the method.
*
* @param configurationData
* the configuration parameters.
* @return extRes the external resource parameters.
*/
protected static Map<String, ExternalResourceDescription> extractResourceParameters(
final Object[] configurationData) {
if (configurationData == null) {
return Collections.emptyMap();
}
Map<String, ExternalResourceDescription> extRes = new HashMap<String, ExternalResourceDescription>();
for (int i = 0; i < configurationData.length - 1; i += 2) {
String key = (String) configurationData[i];
Object value = configurationData[i + 1];
if (value == null) {
continue;
}
// Store External Resource parameters separately
ResourceValueType type = getResourceParameterType(value);
if (type == ResourceValueType.PRIMITIVE) {
ExternalResourceDescription description = (ExternalResourceDescription) value;
extRes.put(key, description);
}
else if (type.isMultiValued()) {
Collection<ExternalResourceDescription> resList;
if (type == ResourceValueType.ARRAY) {
resList = asList((ExternalResourceDescription[]) value);
}
else {
resList = (Collection<ExternalResourceDescription>) value;
}
// Record the list elements
List<Object> params = new ArrayList<Object>();
params.add(ResourceList.PARAM_SIZE);
params.add(String.valueOf(resList.size())); // "Resource" only supports String parameters!
int n = 0;
for (ExternalResourceDescription res : resList) {
params.add(ResourceList.ELEMENT_KEY + "[" + n + "]");
params.add(res);
n++;
}
// Record the list and attach the list elements to the list
extRes.put(key, createResourceDescription(ResourceList.class, params.toArray()));
}
}
return extRes;
}
/**
* Determine which kind of external resource the given value is. This is only meant for
* uimaFIT internal use. This method is required by the ConfigurationParameterFactory, so it is
* package private instead of private.
*/
static ResourceValueType getResourceParameterType(Object aValue) {
if (aValue == null) {
return ResourceValueType.NO_RESOURCE;
}
boolean isResourcePrimitive = aValue instanceof ExternalResourceDescription;
boolean isResourceArray = aValue.getClass().isArray()
&& ExternalResourceDescription.class.isAssignableFrom(aValue.getClass()
.getComponentType());
boolean isResourceCollection = (Collection.class.isAssignableFrom(aValue
.getClass()) && !((Collection) aValue).isEmpty() && ((Collection) aValue)
.iterator().next() instanceof ExternalResourceDescription);
if (isResourcePrimitive) {
return ResourceValueType.PRIMITIVE;
} else if (isResourceArray) {
return ResourceValueType.ARRAY;
} else if (isResourceCollection) {
return ResourceValueType.COLLECTION;
} else {
return ResourceValueType.NO_RESOURCE;
}
}
/**
* Create an instance of a UIMA shared/external resource class.
*
* @param <R>
* the resource type.
* @param resourceClass
* the class implementing the resource.
* @param params
* parameters passed to the resource when it is created. Each parameter consists of two
* arguments, the first being the name and the second being the parameter value
* @return the resource instance.
* @throws ResourceInitializationException
* if there was a problem instantiating the resource.
*/
public static <R extends Resource> R createResource(Class<R> resourceClass,
Object... params) throws ResourceInitializationException {
return createResource(resourceClass, null, params);
}
/**
* Create an instance of a UIMA shared/external resource class.
*
* @param <R>
* the resource type.
* @param resourceClass
* the class implementing the resource.
* @param resMgr
* a resource manager (optional).
* @param params
* parameters passed to the resource when it is created. Each parameter consists of two
* arguments, the first being the name and the second being the parameter value
* @return the resource instance.
* @throws ResourceInitializationException
* if there was a problem instantiating the resource.
*/
@SuppressWarnings("unchecked")
public static <R extends Resource> R createResource(Class<R> resourceClass,
ResourceManager resMgr, Object... params) throws ResourceInitializationException {
ExternalResourceDescription res = createResourceDescription(resourceClass, params);
return (R) produceResource(resourceClass, res.getResourceSpecifier(), resMgr, emptyMap());
}
/**
* Types of external resource values.
*/
static enum ResourceValueType {
NO_RESOURCE,
PRIMITIVE,
ARRAY,
COLLECTION;
public boolean isMultiValued()
{
return this == COLLECTION || this == ARRAY;
}
}
}