diff --git a/biz.aQute.bndlib.tests/test/test/annotationheaders/ServiceProviderFileTest.java b/biz.aQute.bndlib.tests/test/test/annotationheaders/ServiceProviderFileTest.java index 9e4169c810..1d67cad542 100644 --- a/biz.aQute.bndlib.tests/test/test/annotationheaders/ServiceProviderFileTest.java +++ b/biz.aQute.bndlib.tests/test/test/annotationheaders/ServiceProviderFileTest.java @@ -76,4 +76,27 @@ public void testBothMetaInfoAndAnnotations() throws Exception { } } + @Test + public void testBothMetaInfoAndAnnotationsNoParentheses() throws Exception { + try (Builder b = new Builder();) { + b.addClasspath(IO.getFile("bin_test")); + b.setPrivatePackage("test.annotationheaders.spi.providerF"); + b.setProperty("-includeresource", """ + META-INF/services/com.example.service.Type;\ + literal='\ + #import aQute.bnd.annotation.spi.ServiceProvider;\n\ + #@ServiceProvider\n\ + java.lang.String' + """); + b.build(); + assertTrue(b.check()); + Domain manifest = Domain.domain(b.getJar() + .getManifest()); + Parameters provideCapability = manifest.getProvideCapability(); + Parameters requireCapability = manifest.getRequireCapability(); + assertThat(provideCapability.size()).isEqualTo(4); + assertThat(requireCapability.size()).isEqualTo(2); + } + } + } diff --git a/biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java b/biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java index 2c67246f90..efdf15e177 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java +++ b/biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; -import java.lang.annotation.RetentionPolicy; import java.net.URL; import java.time.ZonedDateTime; import java.util.ArrayList; @@ -63,13 +62,11 @@ import aQute.bnd.header.OSGiHeader; import aQute.bnd.header.Parameters; import aQute.bnd.http.HttpClient; -import aQute.bnd.osgi.Annotation.ElementType; import aQute.bnd.osgi.Clazz.JAVA; import aQute.bnd.osgi.Clazz.QUERY; import aQute.bnd.osgi.Descriptors.Descriptor; import aQute.bnd.osgi.Descriptors.PackageRef; import aQute.bnd.osgi.Descriptors.TypeRef; -import aQute.bnd.osgi.MetaInfService.Implementation; import aQute.bnd.service.AnalyzerPlugin; import aQute.bnd.service.ManifestPlugin; import aQute.bnd.service.OrderedPlugin; @@ -220,8 +217,6 @@ private void analyze0() throws Exception { doPlugins(); - analyzeMetaInfServices(); - // // calculate class versions in use // @@ -415,47 +410,6 @@ private void analyze0() throws Exception { } } - /* - * process the META-INF/services/* files. These files can contain bnd - * annotations. - */ - - private void analyzeMetaInfServices() { - try { - MetaInfService.getServiceFiles(getJar()) - .values() - .stream() - .flatMap(mis -> mis.getImplementations() - .values() - .stream()) - .forEach(impl -> { - impl.getAnnotations() - .forEach((annotationName, attrs) -> { - doAnnotationsforMetaInf(impl, Processor.removeDuplicateMarker(annotationName), attrs); - }); - }); - } catch (Exception e) { - exception(e, "failed to process META-INF/services due to %s", e); - } - } - - /* - * Process 1 annotation - */ - private void doAnnotationsforMetaInf(Implementation impl, String annotationName, Attrs attrs) { - try { - Map properties = attrs.toTyped(); - properties.putIfAbsent("value", impl.getServiceName()); // default - TypeRef implementation = getTypeRefFromFQN(impl.getImplementationName()); - assert implementation != null; - Annotation ann = new Annotation(getTypeRefFromFQN(annotationName), properties, ElementType.TYPE, - RetentionPolicy.CLASS); - addAnnotation(ann, implementation); - } catch (Exception e) { - exception(e, "failed to process %s=%v due to %s", annotationName, attrs, e); - } - } - /** * Get the export version of a package * diff --git a/biz.aQute.bndlib/src/aQute/bnd/osgi/Builder.java b/biz.aQute.bndlib/src/aQute/bnd/osgi/Builder.java index 03f9b5c82d..865125d111 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/osgi/Builder.java +++ b/biz.aQute.bndlib/src/aQute/bnd/osgi/Builder.java @@ -50,6 +50,7 @@ import aQute.bnd.metatype.MetatypeAnnotations; import aQute.bnd.osgi.Descriptors.PackageRef; import aQute.bnd.osgi.Descriptors.TypeRef; +import aQute.bnd.osgi.metainf.MetaInfServiceParser; import aQute.bnd.plugin.jpms.JPMSAnnotations; import aQute.bnd.plugin.jpms.JPMSModuleInfoPlugin; import aQute.bnd.plugin.jpms.JPMSMultiReleasePlugin; @@ -1735,6 +1736,7 @@ public Pattern getDoNotCopy() { static JPMSModuleInfoPlugin moduleInfoPlugin = new JPMSModuleInfoPlugin(); static SPIDescriptorGenerator spiDescriptorGenerator = new SPIDescriptorGenerator(); static JPMSMultiReleasePlugin jpmsReleasePlugin = new JPMSMultiReleasePlugin(); + static MetaInfServiceParser metaInfoServiceParser = new MetaInfServiceParser(); @Override protected void setTypeSpecificPlugins(PluginsContainer pluginsContainer) { @@ -1748,6 +1750,7 @@ protected void setTypeSpecificPlugins(PluginsContainer pluginsContainer) { pluginsContainer.add(moduleInfoPlugin); pluginsContainer.add(spiDescriptorGenerator); pluginsContainer.add(jpmsReleasePlugin); + pluginsContainer.add(metaInfoServiceParser); super.setTypeSpecificPlugins(pluginsContainer); } diff --git a/biz.aQute.bndlib/src/aQute/bnd/osgi/MetaInfService.java b/biz.aQute.bndlib/src/aQute/bnd/osgi/metainf/MetaInfService.java similarity index 97% rename from biz.aQute.bndlib/src/aQute/bnd/osgi/MetaInfService.java rename to biz.aQute.bndlib/src/aQute/bnd/osgi/metainf/MetaInfService.java index bf871cdce9..358b7f7927 100644 --- a/biz.aQute.bndlib/src/aQute/bnd/osgi/MetaInfService.java +++ b/biz.aQute.bndlib/src/aQute/bnd/osgi/metainf/MetaInfService.java @@ -1,4 +1,4 @@ -package aQute.bnd.osgi; +package aQute.bnd.osgi.metainf; import java.io.IOException; import java.io.InputStream; @@ -19,6 +19,8 @@ import aQute.bnd.header.Attrs; import aQute.bnd.header.OSGiHeader; import aQute.bnd.header.Parameters; +import aQute.bnd.osgi.Jar; +import aQute.bnd.osgi.Resource; import aQute.lib.io.IO; import aQute.lib.strings.Strings; @@ -44,7 +46,7 @@ public class MetaInfService { static final Pattern IMPORT_P = Pattern .compile("#import\\s+(?" + FQN_S + ")\\s*;?\\s*$"); static final Pattern ANNOTATION_P = Pattern - .compile("#@(?" + FQN_S + ")\\((?.*)" + "\\)\\s*;?\\s*$"); + .compile("#@(?" + FQN_S + ")\\s*(\\((?.*)" + "\\)\\s*)?;?\\s*$"); /** * get the META-INF service files from a JAR diff --git a/biz.aQute.bndlib/src/aQute/bnd/osgi/metainf/MetaInfServiceParser.java b/biz.aQute.bndlib/src/aQute/bnd/osgi/metainf/MetaInfServiceParser.java new file mode 100644 index 0000000000..fd10c7393c --- /dev/null +++ b/biz.aQute.bndlib/src/aQute/bnd/osgi/metainf/MetaInfServiceParser.java @@ -0,0 +1,60 @@ +package aQute.bnd.osgi.metainf; + +import java.lang.annotation.RetentionPolicy; +import java.util.Map; + +import aQute.bnd.header.Attrs; +import aQute.bnd.osgi.Analyzer; +import aQute.bnd.osgi.Annotation; +import aQute.bnd.osgi.Annotation.ElementType; +import aQute.bnd.osgi.Descriptors.TypeRef; +import aQute.bnd.osgi.Processor; +import aQute.bnd.osgi.metainf.MetaInfService.Implementation; +import aQute.bnd.service.AnalyzerPlugin; + +/** + * process the META-INF/services/* files. These files can contain bnd + * annotations. + */ + +public class MetaInfServiceParser implements AnalyzerPlugin { + + /** + * Iterate over the the file in META-INF/services and process them for + * annotations. + */ + @Override + public boolean analyzeJar(Analyzer analyzer) throws Exception { + MetaInfService.getServiceFiles(analyzer.getJar()) + .values() + .stream() + .flatMap(mis -> mis.getImplementations() + .values() + .stream()) + .forEach(impl -> { + impl.getAnnotations() + .forEach((annotationName, attrs) -> { + doAnnotationsforMetaInf(analyzer, impl, Processor.removeDuplicateMarker(annotationName), attrs); + }); + }); + return false; + } + + /* + * Process 1 annotation + */ + private void doAnnotationsforMetaInf(Analyzer analyzer, Implementation impl, String annotationName, Attrs attrs) { + try { + Map properties = attrs.toTyped(); + properties.putIfAbsent("value", impl.getServiceName()); // default + TypeRef implementation = analyzer.getTypeRefFromFQN(impl.getImplementationName()); + assert implementation != null; + Annotation ann = new Annotation(analyzer.getTypeRefFromFQN(annotationName), properties, ElementType.TYPE, + RetentionPolicy.CLASS); + analyzer.addAnnotation(ann, implementation); + } catch (Exception e) { + analyzer.exception(e, "failed to process %s=%v due to %s", annotationName, attrs, e); + } + } + +}