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>

Reply via email to