This is an automated email from the ASF dual-hosted git repository. rec pushed a commit to branch feature/402-Provide-bnd-plugin-to-generate-package-imports-based-on-imports-in-UIMA-descriptors in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git
commit 7e715879a320e99138e3b75142b1539c38f3cbb4 Author: Richard Eckart de Castilho <[email protected]> AuthorDate: Thu Oct 10 17:49:05 2024 +0200 Issue #402: Provide bnd plugin to generate package imports based on imports in UIMA descriptors - Added uima-bnd-plugin - Added documentation --- aggregate-uimaj/pom.xml | 1 + pom.xml | 1 + uima-bnd-plugin/pom.xml | 97 ++++++++++ .../org/apache/uima/tools/bnd/UimaBndPlugin.java | 197 +++++++++++++++++++++ .../src/docs/asciidoc/tools/tools.bnd.adoc | 56 ++++++ uimaj-parent/pom.xml | 7 + 6 files changed, 359 insertions(+) diff --git a/aggregate-uimaj/pom.xml b/aggregate-uimaj/pom.xml index 92936832e..6c4f31ecc 100644 --- a/aggregate-uimaj/pom.xml +++ b/aggregate-uimaj/pom.xml @@ -49,6 +49,7 @@ <module>../PearPackagingMavenPlugin</module> <module>../jcasgen-maven-plugin</module> <module>../uimaj-v3migration-jcas</module> + <module>../uima-bnd-plugin</module> <module>../uimafit-core</module> <module>../uimafit-junit</module> diff --git a/pom.xml b/pom.xml index 6b7d31136..9bfe2eded 100644 --- a/pom.xml +++ b/pom.xml @@ -315,6 +315,7 @@ <exclude>jcasgen-maven-plugin/**</exclude> <exclude>uimaj-adapter-*/**</exclude> <exclude>uimaj-bom/**</exclude> + <exclude>uima-bnd-plugin/**</exclude> <exclude>uimaj-component-test-util/**</exclude> <exclude>uimaj-core/**</exclude> <exclude>uimaj-cpe/**</exclude> diff --git a/uima-bnd-plugin/pom.xml b/uima-bnd-plugin/pom.xml new file mode 100644 index 000000000..6b26f6e67 --- /dev/null +++ b/uima-bnd-plugin/pom.xml @@ -0,0 +1,97 @@ +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.uima</groupId> + <artifactId>uimaj-parent</artifactId> + <version>3.6.0-SNAPSHOT</version> + <relativePath>../uimaj-parent/pom.xml</relativePath> + </parent> + + <artifactId>uima-bnd-plugin</artifactId> + + <name>UIMA Plugin for BND</name> + + <dependencies> + <dependency> + <groupId>org.apache.uima</groupId> + <artifactId>uimaj-core</artifactId> + <version>${project.version}</version> + </dependency> + + <!-- + bndlib is provided at runtime (it neither needs to be considered by the + maven-shade-plugin nor as transitive dependency by Maven) + --> + <dependency> + <groupId>biz.aQute.bnd</groupId> + <artifactId>biz.aQute.bndlib</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <!-- Use a shade plugin in order to append META-INF/service SPI resources into target artifact --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <transformers> + <transformer + implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> + </transformers> + <!-- exclude provided dependencies --> + <artifactSet> + <excludes> + <!-- Don't include anything from bndlib (as this is always + part of the classpath) --> + <exclude>biz.aQute.bnd:bndlib</exclude> + </excludes> + </artifactSet> + <filters> + <filter> + <artifact>org.osgi</artifact> + <!-- exclude everything which is already included in bndlib --> + <excludes> + <exclude>org/osgi/resource</exclude> + <exclude>org/osgi/service/component/annotations</exclude> + <exclude>org/osgi/service/metatype/annotations</exclude> + <exclude>org/osgi/service/repository</exclude> + </excludes> + </filter> + </filters> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/uima-bnd-plugin/src/main/java/org/apache/uima/tools/bnd/UimaBndPlugin.java b/uima-bnd-plugin/src/main/java/org/apache/uima/tools/bnd/UimaBndPlugin.java new file mode 100644 index 000000000..3caf4f3b3 --- /dev/null +++ b/uima-bnd-plugin/src/main/java/org/apache/uima/tools/bnd/UimaBndPlugin.java @@ -0,0 +1,197 @@ +/* + * 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.tools.bnd; + +import static java.util.Collections.emptyList; + +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.uima.UIMAFramework; +import org.apache.uima.analysis_engine.AnalysisEngineDescription; +import org.apache.uima.resource.metadata.FsIndexCollection; +import org.apache.uima.resource.metadata.Import; +import org.apache.uima.resource.metadata.TypePriorities; +import org.apache.uima.resource.metadata.TypeSystemDescription; +import org.apache.uima.util.InvalidXMLException; +import org.apache.uima.util.XMLInputSource; +import org.apache.uima.util.XMLParser; +import org.apache.uima.util.XMLizable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import aQute.bnd.annotation.plugin.BndPlugin; +import aQute.bnd.header.Attrs; +import aQute.bnd.osgi.Analyzer; +import aQute.bnd.osgi.Resource; +import aQute.bnd.service.AnalyzerPlugin; + +@BndPlugin(name = "UIMA") +public class UimaBndPlugin + implements AnalyzerPlugin +{ + + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private static final Pattern XML_FILE = Pattern.compile(".*\\.xml"); + private static final Pattern QN = Pattern + .compile("[_A-Za-z$][_A-Za-z0-9$]*(\\.[_A-Za-z$][_A-Za-z0-9$]*)*"); + + private final static XMLParser PARSER = UIMAFramework.getXMLParser(); + + @Override + public boolean analyzeJar(Analyzer analyzer) throws Exception + { + var jar = analyzer.getJar(); + var resources = jar.getResources(); + + var importsProcessed = 0; + if (resources != null) { + for (var entry : resources.entrySet()) { + var path = entry.getKey(); + var resource = entry.getValue(); + + try { + if (XML_FILE.matcher(path).matches()) { + importsProcessed += analyzeXmlFile(analyzer, path, resource); + } + } + catch (Exception e) { + analyzer.error("Unexpected exception in processing resource (%s): %s", path, e); + } + } + } + + LOG.info("UIMA bnd plugin processed {} imports", importsProcessed); + + return false; + } + + private int analyzeXmlFile(Analyzer analyzer, String path, Resource resource) throws Exception + { + var desc = readUimaDescriptor(resource); + if (desc == null) { + return 0; + } + + LOG.debug("Found {}: {}", desc.getClass().getSimpleName(), path); + var imports = getImportsFromDescriptor(desc); + + var importsProcessed = 0; + for (var imp : imports) { + if (imp.getName() != null) { + handleImportByName(analyzer, path, imp); + importsProcessed++; + continue; + } + + if (imp.getLocation() != null) { + handleImportByLocation(imp); + continue; + } + + LOG.warn( + "Found UIMA type system import without name and location - ignoring, please fix your type system description"); + } + + return importsProcessed; + } + + private void handleImportByLocation(Import imp) + { + LOG.warn( + "Found UIMA type system import by location: {} - ignoring, please only use import-by-name", + imp.getLocation()); + } + + private void handleImportByName(Analyzer analyzer, String path, Import imp) + { + var tsdPackage = imp.getName(); + int lastSeparatorPosition = tsdPackage.lastIndexOf('.'); + if (lastSeparatorPosition >= 0) { + // Cut the name of the XML file and keep only the package + tsdPackage = tsdPackage.substring(0, lastSeparatorPosition); + } + + LOG.debug("Found UIMA type system import by name: {}", tsdPackage); + + var pack = analyzer.getPackageRef(tsdPackage); + if (!QN.matcher(pack.getFQN()).matches()) { + analyzer.warning("Type system import does not seem to refer to a package (%s): %s", + path, pack); + } + + if (!analyzer.getReferred().containsKey(pack)) { + var attrs = new Attrs(); + analyzer.getReferred().put(pack, attrs); + } + } + + private List<Import> getImportsFromDescriptor(XMLizable desc) + { + if (desc instanceof TypeSystemDescription tsd) { + return asList(tsd.getImports()); + } + + if (desc instanceof TypePriorities prio) { + return asList(prio.getImports()); + } + + if (desc instanceof FsIndexCollection idxc) { + return asList(idxc.getImports()); + } + + if (desc instanceof AnalysisEngineDescription aed) { + var imports = new ArrayList<Import>(); + imports.addAll( + getImportsFromDescriptor(aed.getAnalysisEngineMetaData().getTypeSystem())); + imports.addAll( + getImportsFromDescriptor(aed.getAnalysisEngineMetaData().getTypePriorities())); + imports.addAll(getImportsFromDescriptor( + aed.getAnalysisEngineMetaData().getFsIndexCollection())); + return imports; + } + + return emptyList(); + } + + private XMLizable readUimaDescriptor(Resource resource) throws Exception + { + try (var in = resource.openInputStream()) { + return PARSER.parse(new XMLInputSource(in)); + } + catch (InvalidXMLException e) { + // Probably not a type system description - ignore + } + + return null; + } + + private static <T> List<T> asList(T[] aList) + { + if (aList == null) { + return emptyList(); + } + + return Arrays.asList(aList); + } +} \ No newline at end of file diff --git a/uimaj-documentation/src/docs/asciidoc/tools/tools.bnd.adoc b/uimaj-documentation/src/docs/asciidoc/tools/tools.bnd.adoc new file mode 100644 index 000000000..ca9654170 --- /dev/null +++ b/uimaj-documentation/src/docs/asciidoc/tools/tools.bnd.adoc @@ -0,0 +1,56 @@ +// 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. + +[[ugr.tools.bnd]] += UIMA Plugin for bnd + +link:https://bndtools.org[Bnd] is a tooling suite for building OSGi bundles. +Its primary function is generating OSGi meta data by analyzing Java classes. +However, when using UIMA, it may be necessary to add package imports for packages that contain importable XML files such as UIMA type system descriptions to the OSGi metadata. +The UIMA plugin for bnd contributes an analyzer that checks for by-name imports in UIMA XML files and adds the necessary package imports. + +To use the this plugin, specify it in the POM as follows: + +[source,xml,subs="+attributes"] +---- +<plugin> + <groupId>biz.aQute.bnd</groupId> + <artifactId>bnd-maven-plugin</artifactId> + <executions> + <configuration> + <bnd> + -plugin.uima: org.apache.uima.tools.bnd.UimaBndPlugin + </bnd> + </configuration> + </executions> + <dependencies> + <dependency> + <groupId>org.apache.uima</groupId> + <artifactId>uima-bnd-plugin</artifactId> + <version>{revnumber}</version> + </dependency> + </dependencies> +</plugin> +---- + +If the plugin is active during a build, it will log a message like + +---- +[INFO] UIMA bnd plugin processed 5 imports +---- + +If you need more detailed logging, run the Maven build with the `-X` option. diff --git a/uimaj-parent/pom.xml b/uimaj-parent/pom.xml index 041ec4a98..7740b270c 100644 --- a/uimaj-parent/pom.xml +++ b/uimaj-parent/pom.xml @@ -153,6 +153,7 @@ <asciidoctor.version>2.5.10</asciidoctor.version> <asciidoctor.pdf.version>2.3.9</asciidoctor.pdf.version> <assertj-version>3.24.2</assertj-version> + <bnd-version>7.0.0</bnd-version> <commons-csv-version>1.10.0</commons-csv-version> <commons-io-version>2.15.0</commons-io-version> <commons-lang3-version>3.13.0</commons-lang3-version> @@ -367,6 +368,12 @@ <artifactId>javassist</artifactId> <version>3.29.2-GA</version> </dependency> + + <dependency> + <groupId>biz.aQute.bnd</groupId> + <artifactId>biz.aQute.bndlib</artifactId> + <version>${bnd-version}</version> + </dependency> <dependency> <groupId>${eclipseP2RepoId}</groupId>
