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
- Consolidate the different provider interfaces into a TypeSystemProvider interface and provide an abstract base implementation with caching
  • Loading branch information
reckart committed Sep 27, 2024
1 parent e287286 commit cedd071
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.apache.uima.UIMAFramework;
import org.apache.uima.resource.ResourceManager;
import org.apache.uima.resource.metadata.Import;
import org.apache.uima.spi.TypeSystemDescriptionProvider;
import org.apache.uima.spi.TypeSystemProvider;
import org.apache.uima.util.InvalidXMLException;
import org.apache.uima.util.Level;
import org.apache.uima.util.XMLParser;
Expand Down Expand Up @@ -130,9 +130,9 @@ private URL findResouceUrlByName(ResourceManager aResourceManager, String name)

// If that fails, try loading through the SPIs
if (url == null) {
var providers = ServiceLoader.load(TypeSystemDescriptionProvider.class);
var providers = ServiceLoader.load(TypeSystemProvider.class);
for (var provider : providers) {
var maybeTypeSystemUrl = provider.findTypeSystemUrl(name);
var maybeTypeSystemUrl = provider.findResourceUrl(name);
if (maybeTypeSystemUrl.isPresent()) {
url = maybeTypeSystemUrl.get();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,8 @@
*/
package org.apache.uima.spi;

import static java.util.Optional.empty;

import java.net.URL;
import java.util.List;
import java.util.Optional;

import org.apache.uima.resource.metadata.Import;
import org.apache.uima.resource.metadata.TypeSystemDescription;

public interface TypeSystemDescriptionProvider {
Expand All @@ -34,24 +29,4 @@ public interface TypeSystemDescriptionProvider {
* cache the parsed type systems instead of parsing them on every call to this method.
*/
List<TypeSystemDescription> listTypeSystemDescriptions();

/**
* @param aName
* the name of the type system. This should be something like
* {@code some.package.TypeSystem} just as you would put it into the
* {@link Import#setName(String)} of a {@link Import} for a name-based import.
* @return the type system description(s) exported from the given location. The provider should
* resolve any imports in the type systems before returning them.
*
* @apiNote For backwards compatibility, this is currently a default method, so implementations do
* not have to provide it - although by-name importing type systems provided via SPIs
* will not work then unless the type systems are also simply available via the
* classpath. However, in the next major version, the default implementation will be
* removed so implementers have to provide it.a
* @forRemoval 4.0.0 (just the default implementation)
* @since 3.6.0
*/
default Optional<URL> findTypeSystemUrl(String aName) {
return empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.spi;

import static java.util.Optional.empty;

import java.net.URL;
import java.util.Optional;

import org.apache.uima.resource.metadata.Import;

public interface TypeSystemProvider extends TypeSystemDescriptionProvider, JCasClassProvider,
FsIndexCollectionProvider, TypePrioritiesProvider {

/**
* @param aName
* the name of the type system. This should be something like
* {@code some.package.TypeSystem} just as you would put it into the
* {@link Import#setName(String)} of a {@link Import} for a name-based import.
* @return the type system description(s) exported from the given location. The provider should
* resolve any imports in the type systems before returning them.
*
* @apiNote For backwards compatibility, this is currently a default method, so implementations do
* not have to provide it - although by-name importing type systems provided via SPIs
* will not work then unless the type systems are also simply available via the
* classpath. However, in the next major version, the default implementation will be
* removed so implementers have to provide it.a
* @forRemoval 4.0.0 (just the default implementation)
* @since 3.6.0
*/
default Optional<URL> findResourceUrl(String aName) {
return empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* 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.spi;

import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;
import static org.apache.uima.util.TypeSystemUtil.loadTypeSystemDescriptionsFromClasspath;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.apache.uima.jcas.cas.TOP;
import org.apache.uima.resource.metadata.FsIndexCollection;
import org.apache.uima.resource.metadata.TypePriorities;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.util.TypeSystemUtil;

public abstract class TypeSystemProvider_ImplBase implements TypeSystemProvider {

private Set<String> typeSystemPaths = emptySet();
private Set<String> fsIndexCollectionPaths = emptySet();
private Set<String> typePrioritiesPaths = emptySet();

private List<TypeSystemDescription> typeSystemDescriptions;
private List<FsIndexCollection> fsIndexCollections;
private List<TypePriorities> typePriorities;
private List<Class<? extends TOP>> jCasClasses;

protected void setTypeSystemLocations(String... aLocations) {
typeSystemPaths = unmodifiableSet(resolveLocations(aLocations));
}

@Override
public synchronized List<TypeSystemDescription> listTypeSystemDescriptions() {

if (typeSystemDescriptions == null) {
typeSystemDescriptions = loadTypeSystemDescriptionsFromClasspath(getClass(), //
typeSystemPaths.toArray(String[]::new));
}

return typeSystemDescriptions;
}

protected void setFsIndexCollectionLocations(String... aLocations) {
fsIndexCollectionPaths = unmodifiableSet(resolveLocations(aLocations));
}

@Override
public synchronized List<FsIndexCollection> listFsIndexCollections() {

if (fsIndexCollections == null) {
fsIndexCollections = TypeSystemUtil.loadFsIndexCollectionsFromClasspath(getClass(), //
fsIndexCollectionPaths.toArray(String[]::new));
}

return fsIndexCollections;
}

protected void setTypePrioritiesLocations(String... aLocations) {
typePrioritiesPaths = unmodifiableSet(resolveLocations(aLocations));
}

@Override
public synchronized List<TypePriorities> listTypePriorities() {
if (typePriorities == null) {
typePriorities = TypeSystemUtil.loadTypePrioritiesFromClasspath(getClass(), //
typePrioritiesPaths.toArray(String[]::new));
}

return typePriorities;
}

private Set<String> resolveLocations(String... aLocations) {
var paths = new HashSet<String>();
var packagePath = "/" + getClass().getPackage().getName().replace('.', '/') + "/";

for (var location : aLocations) {
var path = location;

// Resolve relative locations to the current package
if (!path.startsWith("/")) {
path = packagePath + path;
}

path += ".xml";

paths.add(path);
}

return paths;
}

@Override
public Optional<URL> findResourceUrl(String aName) {

var fullName = "/" + aName.replace('.', '/') + ".xml";
if (typeSystemPaths.contains(fullName)) {
return ofNullable(getClass().getResource(fullName));
}

return empty();
}

@SuppressWarnings("unchecked")
@Override
public synchronized List<Class<? extends TOP>> listJCasClasses() {

if (jCasClasses == null) {
var classes = new ArrayList<Class<? extends TOP>>();
var cl = getClass().getClassLoader();

for (var tsd : listTypeSystemDescriptions()) {
for (var td : tsd.getTypes()) {
try {
classes.add((Class<? extends TOP>) cl.loadClass(td.getName()));
} catch (ClassNotFoundException e) {
// This is acceptable - there may not be a JCas class
}
}
}
jCasClasses = classes;
}

return jCasClasses;
}
}
Loading

0 comments on commit cedd071

Please sign in to comment.