You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2020/05/07 05:05:58 UTC
[sling-slingfeature-maven-plugin] branch master updated: SLING-9424
: Use dependencies for apis and sources
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-slingfeature-maven-plugin.git
The following commit(s) were added to refs/heads/master by this push:
new 3897400 SLING-9424 : Use dependencies for apis and sources
3897400 is described below
commit 3897400666730694fff393c84ea2f60cc3960596
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Thu May 7 07:05:48 2020 +0200
SLING-9424 : Use dependencies for apis and sources
---
.../sling/feature/maven/mojos/ApisJarMojo.java | 312 +++++++++++++++------
.../maven/mojos/{ => apis}/ApisJarContext.java | 145 +++++++++-
.../sling/feature/maven/mojos/apis/ApisUtil.java | 82 ++++++
.../maven/mojos/{ => apis}/JavadocExecutor.java | 4 +-
4 files changed, 438 insertions(+), 105 deletions(-)
diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java b/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java
index ee4f141..e5f3872 100644
--- a/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java
@@ -18,7 +18,9 @@ package org.apache.sling.feature.maven.mojos;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileWriter;
import java.io.IOException;
+import java.io.Writer;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -86,7 +88,10 @@ import org.apache.sling.feature.extension.apiregions.api.ApiExport;
import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
import org.apache.sling.feature.io.IOUtils;
-import org.apache.sling.feature.maven.mojos.ApisJarContext.ArtifactInfo;
+import org.apache.sling.feature.maven.mojos.apis.ApisJarContext;
+import org.apache.sling.feature.maven.mojos.apis.ApisJarContext.ArtifactInfo;
+import org.apache.sling.feature.maven.mojos.apis.ApisUtil;
+import org.apache.sling.feature.maven.mojos.apis.JavadocExecutor;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.jar.JarArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
@@ -106,31 +111,41 @@ import org.osgi.framework.Constants;
)
public class ApisJarMojo extends AbstractIncludingFeatureMojo {
- /** Alternative ID to a source artifact. */
- private static final String SCM_ID = "source-ids";
+ public enum ArtifactType {
+ APIS("apis", "class", "jar"),
+ SOURCES("sources", "java", "jar"),
+ JAVADOC("javadoc", "html", "jar"),
+ DEPENDENCIES("apideps", "txt", "ref"),
+ CND("cnd", "cnd", "jar");
- /** Tag for source when using SCM info */
- private static final String SCM_TAG = "scm-tag";
+ private final String id;
- /** Alternative SCM location. */
- private static final String SCM_LOCATION = "scm-location";
+ private final String type;
- /** Alternative SCM encoding, default is UTF-8 */
- private static final String SCM_ENCODING = "scm-encoding";
+ private final String extension;
- private static final String APIS = "apis";
-
- private static final String SOURCES = "sources";
-
- private static final String JAVADOC = "javadoc";
+ ArtifactType(final String id, final String type, final String extension) {
+ this.id = id;
+ this.type = type;
+ this.extension = extension;
+ }
- private static final String JAR_TYPE = "jar";
+ public String getId() {
+ return this.id;
+ }
- private static final String JAVA_EXTENSION = ".java";
+ public String getContentType() {
+ return this.type;
+ }
- private static final String CLASS_EXTENSION = ".class";
+ public String getContentExtension() {
+ return ".".concat(this.type);
+ }
- private static final String CND_EXTENSION = ".cnd";
+ public String getExtension() {
+ return this.extension;
+ }
+ }
/**
* Select the features for api generation.
@@ -279,6 +294,21 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
@Parameter
private String licenseReportFooter;
+ /**
+ * If enabled, packages from artifacts which are fully consumed (all public api) are omitted from the api and source jars
+ * and a dependency list is generated instead.
+ * @since 1.3.0
+ */
+ @Parameter(defaultValue = "false")
+ private boolean useApiDependencies;
+
+ /**
+ * Comma separated list of Maven repository lists. If set, and {@link #useApiDependencies} is enabled,
+ * then one of the listed repositories must provide the artifact.
+ * @since 1.3.0
+ */
+ @Parameter
+ private String apiRepositoryUrls;
@Parameter(defaultValue = "${project.build.directory}/apis-jars", readonly = true)
private File mainOutputDir;
@@ -460,6 +490,7 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
final File featureDir = new File(mainOutputDir, feature.getId().getArtifactId());
final ApisJarContext ctx = new ApisJarContext(this.mainOutputDir, feature.getId(), regions);
ctx.setLicenseDefaults(licenseDefaults);
+ ctx.setDependencyRepositories(this.apiRepositoryUrls);
// for each bundle included in the feature file and record directories
for (final Artifact artifact : feature.getBundles()) {
@@ -469,26 +500,30 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
ctx.getPackagesWithoutJavaClasses().forEach( p -> getLog().info("Exported package " + p + " does not contain any java classes"));
ctx.getPackagesWithoutSources().forEach( p -> getLog().info("Exported package " + p + " does not have sources"));
- // recollect and package stuff
+ // recollect and package stuff per region
for (final ApiRegion apiRegion : regions.listRegions()) {
final File regionDir = new File(featureDir, apiRegion.getName());
if (generateApiJar) {
- final File apiJar = createArchive(ctx, apiRegion, APIS, this.apiResources);
- report(apiJar, APIS, apiRegion, "class", ctx);
+ final File apiJar = createArchive(ctx, apiRegion, ArtifactType.APIS, this.apiResources, this.useApiDependencies);
+ report(ctx, apiJar, ArtifactType.APIS, apiRegion, this.useApiDependencies);
}
if (generateSourceJar) {
- final File sourceJar = createArchive(ctx, apiRegion, SOURCES, this.apiSourceResources);
- report(sourceJar, SOURCES, apiRegion, "java", ctx);
+ final File sourceJar = createArchive(ctx, apiRegion, ArtifactType.SOURCES, this.apiSourceResources, this.useApiDependencies);
+ report(ctx, sourceJar, ArtifactType.SOURCES, apiRegion, this.useApiDependencies);
+ }
+
+ if ( this.useApiDependencies && (this.generateApiJar || this.generateSourceJar)) {
+ this.createDependenciesFile(ctx, apiRegion);
}
if (generateJavadocJar) {
- final File javadocsDir = new File(regionDir, JAVADOC);
+ final File javadocsDir = new File(regionDir, ArtifactType.JAVADOC.getId());
if ( generateJavadoc(ctx, apiRegion, javadocsDir) ) {
ctx.setJavadocDir(javadocsDir);
- final File javadocJar = createArchive(ctx, apiRegion, JAVADOC, this.apiJavadocResources);
- report(javadocJar, JAVADOC, apiRegion, "html", ctx);
+ final File javadocJar = createArchive(ctx, apiRegion, ArtifactType.JAVADOC, this.apiJavadocResources, false);
+ report(ctx, javadocJar, ArtifactType.JAVADOC, apiRegion, false);
} else {
getLog().warn("Javadoc JAR will NOT be generated - sources directory " + ctx.getDeflatedSourcesDir()
+ " was empty or contained no Java files!");
@@ -500,23 +535,37 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
.a(" succesfully created").toString());
}
- private void report(final File jarFile, final String apiType, final ApiRegion apiRegion, final String extension, ApisJarContext ctx) throws MojoExecutionException {
- final List<String> packages = getPackages(jarFile, extension);
+ private void report(final ApisJarContext ctx,
+ final File jarFile,
+ final ArtifactType artifactType,
+ final ApiRegion apiRegion,
+ final boolean omitDependencyArtifacts)
+ throws MojoExecutionException {
+ final List<String> packages = getPackages(jarFile, artifactType.getContentExtension());
+ if ( omitDependencyArtifacts ) {
+ for(final ArtifactInfo info : ctx.getArtifactInfos(apiRegion, false)) {
+ if ( info.isUseAsDependencyPerRegion(apiRegion) ) {
+ for(final Clause c : info.getUsedExportedPackages(apiRegion) ) {
+ packages.add(c.getName());
+ }
+ }
+ }
+ }
final List<ApiExport> missing = new ArrayList<>();
// for the report we always use the binaries as the source of truth
// that's why we check against getPackagesWithoutJavaClasses()
for (final ApiExport exp : apiRegion.listExports()) {
- String packageName = exp.getName();
+ final String packageName = exp.getName();
if (!packages.remove(packageName) && !ctx.getPackagesWithoutJavaClasses().contains(packageName)) {
missing.add(exp);
}
}
if (missing.isEmpty() && packages.isEmpty()) {
- getLog().info("Verified " + apiType + " jar for region " + apiRegion.getName());
+ getLog().info("Verified " + artifactType.getId() + " jar for region " + apiRegion.getName());
} else {
Collections.sort(missing);
- getLog().info(apiType + " jar for region " + apiRegion.getName() + " has " + ( missing.size() + packages.size() ) + " errors:");
+ getLog().info(artifactType.getId() + " jar for region " + apiRegion.getName() + " has " + ( missing.size() + packages.size() ) + " errors:");
for (final ApiExport m : missing) {
final List<String> candidates = new ArrayList<>();
for(final ArtifactInfo info : ctx.getArtifactInfos()) {
@@ -569,6 +618,34 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
}
}
+ private boolean calculateOmitDependenciesFlag(final Clause[] exportedPackageClauses, final Set<Clause> usedExportedPackagesPerRegion) {
+ // check whether all packages are exported in this region
+ boolean fullUsage = true;
+ for(final Clause c : exportedPackageClauses) {
+ boolean found = false;
+ for(final Clause current : usedExportedPackagesPerRegion) {
+ if ( current.getName().equals(c.getName()) ) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ fullUsage = false;
+ break;
+ }
+ }
+
+ return fullUsage;
+ }
+
+ /**
+ * Process a single artifact.
+ * This is a "global" processing and not per region
+ * @param ctx The context
+ * @param artifact The artifact
+ * @throws MojoExecutionException
+ */
private void onArtifact(final ApisJarContext ctx, final Artifact artifact)
throws MojoExecutionException {
final File bundleFile = getArtifactFile(artifactProvider, artifact.getId());
@@ -586,13 +663,25 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
final Set<String> usedExportedPackages = computeUsedExportPackages(ctx.getApiRegions(), exportedPackageClauses, artifact.getId());
if ( !usedExportedPackages.isEmpty()) {
- final ArtifactInfo info = ctx.addArtifactInfo(artifact.getId());
+ final ArtifactInfo info = ctx.addArtifactInfo(artifact);
info.setUsedExportedPackages(usedExportedPackages);
// calculate per region packages
for(final ApiRegion region : ctx.getApiRegions().listRegions()) {
final Set<Clause> usedExportedPackagesPerRegion = computeUsedExportPackages(region, exportedPackageClauses, artifact.getId());
- info.setUsedExportedPackages(region, usedExportedPackagesPerRegion);
+
+ // check whether packages are included in api jars - or added as a dependency
+ boolean useAsDependency = this.useApiDependencies ? calculateOmitDependenciesFlag(exportedPackageClauses, usedExportedPackagesPerRegion) : false;
+ if ( useAsDependency ) {
+ useAsDependency = ctx.findDependencyArtifact(getLog(), info);
+ if ( useAsDependency ) {
+ // check scm info
+ if ( artifact.getMetadata().get(ApisUtil.SCM_LOCATION) != null ) {
+ throw new MojoExecutionException("Dependency artifact must not specify " + ApisUtil.SCM_LOCATION + " : " + artifact.getId().toMvnId());
+ }
+ }
+ }
+ info.setUsedExportedPackages(region, usedExportedPackagesPerRegion, useAsDependency);
}
info.setBinDirectory(new File(ctx.getDeflatedBinDir(), info.getId().toMvnName()));
@@ -657,7 +746,7 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
if ( (generateSourceJar || generateJavadocJar) ) {
final Set<String> foundPackages = new HashSet<>();
if ( info.getSourceDirectory() != null && info.getSourceDirectory().exists()) {
- final String encoding = artifact.getMetadata().getOrDefault(SCM_ENCODING, "UTF-8");
+ final String encoding = artifact.getMetadata().getOrDefault(ApisUtil.SCM_ENCODING, "UTF-8");
this.postProcessSourcesDirectory(ctx, info, foundPackages, info.getSourceDirectory(), "UTF-8".equals(encoding) ? null : encoding, "");
}
// check for missing packages
@@ -678,7 +767,7 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
for(final File child : dir.listFiles()) {
if ( child.isDirectory() ) {
postProcessBinDirectory(ctx, info, child, pck.isEmpty() ? child.getName() : pck.concat(".").concat(child.getName()));
- } else if ( child.getName().endsWith(CLASS_EXTENSION)) {
+ } else if ( child.getName().endsWith(ArtifactType.APIS.getContentExtension())) {
hasJavaFile = true;
}
}
@@ -780,7 +869,7 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
postProcessSourcesDirectory(ctx, info,
foundPackages, child, readEncoding,
pck.isEmpty() ? child.getName() : pck.concat(".").concat(child.getName()));
- } else if ( child.getName().endsWith(JAVA_EXTENSION)) {
+ } else if ( child.getName().endsWith(ArtifactType.SOURCES.getContentExtension())) {
hasSourceFile = true;
if (readEncoding != null ) {
try {
@@ -1037,7 +1126,7 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
}
}
}
- if ( resourceName.endsWith(CND_EXTENSION)) {
+ if ( resourceName.endsWith(ArtifactType.CND.getContentExtension())) {
info.getNodeTypes().add(resourceName);
}
}
@@ -1082,18 +1171,16 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
ArtifactId artifactId = artifact.getId();
getLog().debug("Downloading sources for " + artifactId.toMvnId() + "...");
- String scmId = artifact.getMetadata().get(SCM_ID);
- String scmLocation = artifact.getMetadata().get(SCM_LOCATION);
- if ( scmId != null && scmLocation != null) {
- throw new MojoExecutionException("Both " + SCM_ID + " and " + SCM_LOCATION + " are defined for " + artifactId);
+ List<ArtifactId> scmIds = ApisUtil.getApiIds(artifact);
+ String scmLocation = artifact.getMetadata().get(ApisUtil.SCM_LOCATION);
+ if ( scmIds != null && scmLocation != null) {
+ throw new MojoExecutionException("Both " + ApisUtil.SCM_IDS + " and " + ApisUtil.SCM_LOCATION + " are defined for " + artifactId);
}
boolean fallbackToScmCheckout = false;
- if ( scmId != null ) {
- final String value = scmId;
- for (final String id : value.split(",")) {
- final ArtifactId sourcesArtifactId = ArtifactId.parse(id);
+ if ( scmIds != null ) {
+ for(final ArtifactId sourcesArtifactId : scmIds) {
downloadSourceAndDeflate(ctx, info, sourcesArtifactId, false);
}
} else if ( scmLocation != null ) {
@@ -1143,8 +1230,8 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
final Artifact sourceArtifact)
throws MojoExecutionException {
// fallback to Artifacts SCM metadata first
- String connection = sourceArtifact.getMetadata().get(SCM_LOCATION);
- String tag = sourceArtifact.getMetadata().get(SCM_TAG);
+ String connection = sourceArtifact.getMetadata().get(ApisUtil.SCM_LOCATION);
+ String tag = sourceArtifact.getMetadata().get(ApisUtil.SCM_TAG);
// Artifacts SCM metadata may not available or are an override, let's fallback to the POM
getLog().debug("Falling back to SCM checkout...");
@@ -1325,10 +1412,10 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
return result;
}
- private String getApiExportClause(final ApisJarContext ctx, final ApiRegion region) {
+ private String getApiExportClause(final ApiRegion region, final Collection<ArtifactInfo> infos) {
final StringBuilder sb = new StringBuilder();
boolean first = true;
- for(final ArtifactInfo info : ctx.getArtifactInfos()) {
+ for(final ArtifactInfo info : infos) {
for(final Clause clause : info.getUsedExportedPackages(region)) {
if (first) {
first = false;
@@ -1343,16 +1430,18 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
private File createArchive(final ApisJarContext ctx,
final ApiRegion apiRegion,
- final String classifier,
- final List<File> resources) throws MojoExecutionException {
+ final ArtifactType archiveType,
+ final List<File> resources,
+ final boolean omitDependencyArtifacts) throws MojoExecutionException {
final JarArchiver jarArchiver = new JarArchiver();
- final Collection<ArtifactInfo> infos = ctx.getArtifactInfos(apiRegion);
+ // get all artifacts for this region
+ final Collection<ArtifactInfo> infos = ctx.getArtifactInfos(apiRegion, omitDependencyArtifacts);
- if ( APIS.equals(classifier) || SOURCES.equals(classifier) ) {
+ if ( archiveType == ArtifactType.APIS || archiveType == ArtifactType.SOURCES ) {
// api or source
for(final ArtifactInfo info : infos) {
- final File dir = APIS.equals(classifier) ? info.getBinDirectory() : info.getSourceDirectory();
+ final File dir = archiveType == ArtifactType.APIS ? info.getBinDirectory() : info.getSourceDirectory();
final String[] usedExportedPackageIncludes = info.getUsedExportedPackageIncludes(apiRegion);
getLog().debug("Adding directory " + dir.getName() + " with " + Arrays.toString(usedExportedPackageIncludes));
@@ -1368,15 +1457,12 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
}
// add included resources
- for(final ArtifactInfo includeEntry : ctx.getArtifactInfos()) {
- final Set<Clause> usedExportedPackageIncludes = includeEntry.getUsedExportedPackages(apiRegion);
- if ( !usedExportedPackageIncludes.isEmpty() ) {
- final int prefixLength = includeEntry.getBinDirectory().getAbsolutePath().length() + 1;
- for(final File resource : includeEntry.getIncludedResources()) {
- final String name = resource.getAbsolutePath().substring(prefixLength);
- jarArchiver.addFile(resource, name);
- getLog().debug("Adding resource " + name);
- }
+ for(final ArtifactInfo info : infos) {
+ final int prefixLength = info.getBinDirectory().getAbsolutePath().length() + 1;
+ for(final File resource : info.getIncludedResources()) {
+ final String name = resource.getAbsolutePath().substring(prefixLength);
+ jarArchiver.addFile(resource, name);
+ getLog().debug("Adding resource " + name);
}
}
@@ -1405,39 +1491,24 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
jarArchiver.addFile(out, this.licenseReport);
}
- // build classifier
- final StringBuilder classifierBuilder = new StringBuilder();
- if (ctx.getFeatureId().getClassifier() != null) {
- classifierBuilder.append(mapApiClassifier(ctx.getFeatureId().getClassifier()))
- .append('-');
- }
- final String finalClassifier = classifierBuilder.append(mapApiRegionName(apiRegion.getName()))
- .append('-')
- .append(classifier)
- .toString();
-
- final String artifactName = String.format("%s-%s", project.getArtifactId(), finalClassifier);
-
- final ArtifactId apiId = apiVersion == null ? ctx.getFeatureId() : ctx.getFeatureId().changeVersion(this.apiVersion);
+ final ArtifactId targetId = this.buildArtifactId(ctx, apiRegion, archiveType);
+ final String artifactName = String.format("%s-%s", targetId.getArtifactId(), targetId.getClassifier());
MavenArchiveConfiguration archiveConfiguration = new MavenArchiveConfiguration();
archiveConfiguration.setAddMavenDescriptor(false);
- if (APIS.equals(classifier)) {
+ if (archiveType == ArtifactType.APIS) {
// APIs need OSGi Manifest entry
String symbolicName = artifactName.replace('-', '.');
- archiveConfiguration.addManifestEntry("Export-Package", getApiExportClause(ctx, apiRegion));
+ archiveConfiguration.addManifestEntry("Export-Package", getApiExportClause(apiRegion, infos));
archiveConfiguration.addManifestEntry("Bundle-Description", project.getDescription());
- archiveConfiguration.addManifestEntry("Bundle-Version", apiId.getOSGiVersion().toString());
+ archiveConfiguration.addManifestEntry("Bundle-Version", targetId.getOSGiVersion().toString());
archiveConfiguration.addManifestEntry("Bundle-ManifestVersion", "2");
archiveConfiguration.addManifestEntry("Bundle-SymbolicName", symbolicName);
archiveConfiguration.addManifestEntry("Bundle-Name", artifactName);
final Set<String> nodeTypes = new HashSet<>();
- for(final ArtifactInfo includeEntry : ctx.getArtifactInfos()) {
- final Set<Clause> usedExportedPackageIncludes = includeEntry.getUsedExportedPackages(apiRegion);
- if ( !usedExportedPackageIncludes.isEmpty() ) {
- nodeTypes.addAll(includeEntry.getNodeTypes());
- }
+ for(final ArtifactInfo info : infos) {
+ nodeTypes.addAll(info.getNodeTypes());
}
if (!nodeTypes.isEmpty()) {
archiveConfiguration.addManifestEntry("Sling-Nodetypes", String.join(",", nodeTypes));
@@ -1450,8 +1521,8 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
archiveConfiguration.addManifestEntry("Provide-Capability", "osgi.unresolvable");
archiveConfiguration.addManifestEntry("Require-Capability", "osgi.unresolvable;filter:=\"(&(must.not.resolve=*)(!(must.not.resolve=*)))\",osgi.ee;filter:=\"(&(osgi.ee=JavaSE/compact2)(version=1.8))\"");
}
- archiveConfiguration.addManifestEntry("Implementation-Version", apiId.getVersion());
- archiveConfiguration.addManifestEntry("Specification-Version", apiId.getVersion());
+ archiveConfiguration.addManifestEntry("Implementation-Version", targetId.getVersion());
+ archiveConfiguration.addManifestEntry("Specification-Version", targetId.getVersion());
archiveConfiguration.addManifestEntry("Implementation-Title", artifactName);
archiveConfiguration.addManifestEntry("Specification-Title", artifactName);
@@ -1460,8 +1531,7 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
archiveConfiguration.addManifestEntry("Specification-Vendor", project.getOrganization().getName());
}
- String targetName = String.format("%s-%s-%s.jar", project.getArtifactId(), project.getVersion(), finalClassifier);
- File target = new File(mainOutputDir, targetName);
+ final File target = new File(mainOutputDir, targetId.toMvnName());
MavenArchiver archiver = new MavenArchiver();
archiver.setArchiver(jarArchiver);
archiver.setOutputFile(target);
@@ -1469,7 +1539,7 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
try {
archiver.createArchive(mavenSession, project, archiveConfiguration);
if (this.attachApiJars) {
- projectHelper.attachArtifact(project, JAR_TYPE, finalClassifier, target);
+ projectHelper.attachArtifact(project, targetId.getType(), targetId.getClassifier(), target);
}
} catch (Exception e) {
throw new MojoExecutionException("An error occurred while creating APIs "
@@ -1480,6 +1550,65 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
return target;
}
+ private ArtifactId buildArtifactId(final ApisJarContext ctx, final ApiRegion apiRegion, final ArtifactType artifactType) {
+ final StringBuilder classifierBuilder = new StringBuilder();
+ if (ctx.getFeatureId().getClassifier() != null) {
+ classifierBuilder.append(mapApiClassifier(ctx.getFeatureId().getClassifier()))
+ .append('-');
+ }
+ final String finalClassifier = classifierBuilder.append(mapApiRegionName(apiRegion.getName()))
+ .append('-')
+ .append(artifactType.getId())
+ .toString();
+
+ return ctx.getFeatureId()
+ .changeClassifier(finalClassifier)
+ .changeType(artifactType.getExtension())
+ .changeVersion(this.apiVersion != null ? this.apiVersion : ctx.getFeatureId().getVersion());
+ }
+
+ /**
+ * Create the dependencies file for a region
+ * @param ctx The context
+ * @param apiRegion The region
+ */
+ private void createDependenciesFile(final ApisJarContext ctx, final ApiRegion apiRegion) throws MojoExecutionException {
+ final Collection<ArtifactInfo> infos = ctx.getArtifactInfos(apiRegion, false);
+
+ final List<ArtifactId> dependencies = new ArrayList<>();
+
+ for(final ArtifactInfo info : infos) {
+ if ( info.isUseAsDependencyPerRegion(apiRegion) ) {
+ dependencies.addAll(info.getDependencyArtifacts());
+ }
+ }
+ Collections.sort(dependencies);
+
+ final ArtifactId targetId = this.buildArtifactId(ctx, apiRegion, ArtifactType.DEPENDENCIES);
+ final File target = new File(mainOutputDir, targetId.toMvnName());
+
+ if ( !dependencies.isEmpty() ) {
+ getLog().info("Writing dependencies file ".concat(target.getAbsolutePath()));
+ try ( final Writer w = new FileWriter(target)) {
+ for(final ArtifactId id : dependencies) {
+ w.write(id.toMvnId());
+ w.write(System.lineSeparator());
+ }
+ } catch (final IOException e) {
+ throw new MojoExecutionException("Unable to write dependencies file", e);
+ }
+ if (this.attachApiJars) {
+ projectHelper.attachArtifact(project, targetId.getType(), targetId.getClassifier(), target);
+ }
+ } else {
+ getLog().info("No dependencies found");
+ if ( target.exists() ) {
+ target.delete();
+ }
+ }
+ }
+
+
/**
* Generate the javadoc
* @param ctx The generation context
@@ -1574,12 +1703,11 @@ public class ApisJarMojo extends AbstractIncludingFeatureMojo {
}
private List<String> getPackages(final File file, final String extension) throws MojoExecutionException {
- final String postfix = ".".concat(extension);
final Set<String> packages = new HashSet<>();
try (final JarInputStream jis = new JarInputStream(new FileInputStream(file))) {
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
- if (entry.getName().endsWith(postfix)) {
+ if (entry.getName().endsWith(extension)) {
final int lastPos = entry.getName().lastIndexOf('/');
if (lastPos != -1) {
packages.add(entry.getName().substring(0, lastPos).replace('/', '.'));
diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarContext.java b/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisJarContext.java
similarity index 57%
rename from src/main/java/org/apache/sling/feature/maven/mojos/ApisJarContext.java
rename to src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisJarContext.java
index aa1359e..5e15df9 100644
--- a/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarContext.java
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisJarContext.java
@@ -14,9 +14,12 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.sling.feature.maven.mojos;
+package org.apache.sling.feature.maven.mojos.apis;
import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -30,6 +33,8 @@ import org.apache.felix.utils.manifest.Clause;
import org.apache.maven.model.License;
import org.apache.maven.model.Model;
import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
@@ -38,14 +43,14 @@ import org.apache.sling.feature.maven.mojos.selection.IncludeExcludeMatcher;
/**
* Context for creating the api jars
*/
-class ApisJarContext {
+public class ApisJarContext {
/**
* Information about a single artifact (bundle) taking part in the api generation.
*/
public static final class ArtifactInfo {
- private ArtifactId id;
+ private Artifact artifact;
private File binDirectory;
@@ -57,18 +62,25 @@ class ApisJarContext {
/** Exported packages per region. */
private final Map<String, Set<Clause>> usedExportedPackagesRegion = new HashMap<>();
+ /** Flag if used as dependency */
+ private final Map<String, Boolean> useAsDependencyPerRegion = new HashMap<>();
+
private final Set<File> includedResources = new HashSet<>();
private final Set<String> nodeTypes = new HashSet<>();
private List<License> licenses;
- public ArtifactInfo(final ArtifactId id) {
- this.id = id;
+ public ArtifactInfo(final Artifact artifact) {
+ this.artifact = artifact;
}
public ArtifactId getId() {
- return this.id;
+ return this.artifact.getId();
+ }
+
+ public Artifact getArtifact() {
+ return this.artifact;
}
public File getBinDirectory() {
@@ -107,8 +119,9 @@ class ApisJarContext {
return this.usedExportedPackagesRegion.get(region.getName());
}
- public void setUsedExportedPackages(final ApiRegion region, final Set<Clause> usedExportedPackages) {
+ public void setUsedExportedPackages(final ApiRegion region, final Set<Clause> usedExportedPackages, final boolean useAsDependency) {
this.usedExportedPackagesRegion.put(region.getName(), usedExportedPackages);
+ this.useAsDependencyPerRegion.put(region.getName(), useAsDependency);
}
public String[] getUsedExportedPackageIncludes(final ApiRegion region) {
@@ -120,6 +133,10 @@ class ApisJarContext {
return includes.toArray(new String[includes.size()]);
}
+ public boolean isUseAsDependencyPerRegion(final ApiRegion region) {
+ return this.useAsDependencyPerRegion.get(region.getName());
+ }
+
public Set<File> getIncludedResources() {
return includedResources;
}
@@ -139,6 +156,39 @@ class ApisJarContext {
public void setLicenses(List<License> licenses) {
this.licenses = licenses;
}
+
+ /**
+ * Get the dependency artifacts
+ * <ol>
+ * <li>If {@code ApisUtil#API_IDS} is provided as metadata for the artifact,
+ * the value is used as a list of artifacts
+ * <li>If {@code ApisUtil#SCM_IDS} is provided as metadata for the artifact,
+ * the value is used as a list of artifacts. The artifacts must have a classifier
+ * set. The classifier is removed and then the artifacts are used
+ * <li>The artifact itself is used
+ * </ol>
+ * @return The list of dependency artifacts
+ * @throws MojoExecutionException
+ */
+ public List<ArtifactId> getDependencyArtifacts() throws MojoExecutionException {
+ final List<ArtifactId> dependencies = new ArrayList<>();
+ final List<ArtifactId> apiIds = ApisUtil.getApiIds(artifact);
+ if ( apiIds != null ) {
+ for(final ArtifactId id : apiIds) {
+ dependencies.add(id);
+ }
+ } else {
+ final List<ArtifactId> sourceIds = ApisUtil.getSourceIds(artifact);
+ if ( sourceIds != null ) {
+ for(final ArtifactId id : sourceIds) {
+ dependencies.add(id.changeClassifier(null));
+ }
+ } else {
+ dependencies.add(getId());
+ }
+ }
+ return dependencies;
+ }
}
private final Set<String> javadocClasspath = new HashSet<>();
@@ -165,6 +215,9 @@ class ApisJarContext {
private IncludeExcludeMatcher licenseDefaultMatcher;
+ /** The set of dependency repositories (URLs) */
+ private Set<String> dependencyRepositories = new HashSet<>();
+
public ApisJarContext(final File mainDir, final ArtifactId featureId, final ApiRegions regions) {
this.featureId = featureId;
@@ -219,8 +272,8 @@ class ApisJarContext {
return packagesWithoutSources;
}
- public ArtifactInfo addArtifactInfo(final ArtifactId id) {
- final ArtifactInfo info = new ArtifactInfo(id);
+ public ArtifactInfo addArtifactInfo(final Artifact artifact) {
+ final ArtifactInfo info = new ArtifactInfo(artifact);
this.infos.add(info);
return info;
@@ -234,11 +287,13 @@ class ApisJarContext {
return this.modelCache;
}
- public Collection<ArtifactInfo> getArtifactInfos(final ApiRegion region) {
+ public Collection<ArtifactInfo> getArtifactInfos(final ApiRegion region, final boolean omitDependencyArtifacts) {
final Map<ArtifactId, ArtifactInfo> result = new TreeMap<>();
for(final ArtifactInfo info : this.infos) {
if ( !info.getUsedExportedPackages(region).isEmpty() ) {
- result.put(info.getId(), info);
+ if ( !omitDependencyArtifacts || !info.isUseAsDependencyPerRegion(region) ) {
+ result.put(info.getId(), info);
+ }
}
}
return result.values();
@@ -252,4 +307,72 @@ class ApisJarContext {
public String getLicenseDefault(final ArtifactId id) {
return this.licenseDefaultMatcher.matches(id);
}
+
+ /**
+ * Set the dependency repositories
+ * @param list Comma separated list or {@code null}
+ */
+ public void setDependencyRepositories(final String list) {
+ this.dependencyRepositories.clear();
+ if ( list != null ) {
+ for(String val : list.split(",") ) {
+ val = val.trim();
+ if ( !val.endsWith("/") ) {
+ val = val.concat("/");
+ }
+ this.dependencyRepositories.add(val);
+ }
+ }
+ }
+
+ /**
+ * Find a an artifact
+ * If dependency repositories are configured, one of them must provide the artifact
+ * @param log Logger
+ * @param id The artifact id
+ * @return {@code true} if the artifact could be found.
+ * @throws MojoExecutionException
+ */
+ private boolean findDependencyArtifact(final Log log, final ArtifactId id) throws MojoExecutionException {
+ boolean result = true;
+ if ( !this.dependencyRepositories.isEmpty() ) {
+ result = false;
+ log.debug("Trying to resolve ".concat(id.toMvnId()).concat(" from ").concat(this.dependencyRepositories.toString()));
+ for(final String server : this.dependencyRepositories) {
+ try {
+ final URL url = new URL(server.concat(id.toMvnPath()));
+ try {
+ url.openConnection().getInputStream();
+ log.debug("Found ".concat(id.toMvnId()).concat(" at ").concat(url.toString()));
+ result = true;
+ break;
+ } catch (IOException e) {
+ // not available
+ log.debug("Missed ".concat(id.toMvnId()).concat(" at ").concat(url.toString()).concat(" : ").concat(e.toString()));
+ }
+ } catch ( final MalformedURLException mue) {
+ throw new MojoExecutionException("Unable to find dependency on ".concat(server), mue);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Check if all dependency artifacts can be found
+ * @param log The logger
+ * @param info The artifact info
+ * @return {@code true} if all artifacts are publically available
+ * @throws MojoExecutionException
+ */
+ public boolean findDependencyArtifact(final Log log, final ArtifactInfo info) throws MojoExecutionException {
+ boolean result = true;
+ for(final ArtifactId id : info.getDependencyArtifacts()) {
+ if ( !findDependencyArtifact(log, id) ) {
+ result = false;
+ break;
+ }
+ }
+ return result;
+ }
}
diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisUtil.java b/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisUtil.java
new file mode 100644
index 0000000..e8036b3
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisUtil.java
@@ -0,0 +1,82 @@
+/*
+ * 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.sling.feature.maven.mojos.apis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
+
+/**
+ * Context for creating the api jars
+ */
+public class ApisUtil {
+
+ /** Tag for source when using SCM info */
+ public static final String SCM_TAG = "scm-tag";
+
+ /** Alternative SCM location. */
+ public static final String SCM_LOCATION = "scm-location";
+
+ /** Alternative SCM encoding, default is UTF-8 */
+ public static final String SCM_ENCODING = "scm-encoding";
+
+ /** Alternative IDs for artifact dependencies. */
+ public static final String API_IDS = "api-ids";
+
+ /** Alternative IDS to a source artifact. */
+ public static final String SCM_IDS = "source-ids";
+
+ public static List<ArtifactId> getSourceIds(final Artifact artifact) throws MojoExecutionException {
+ final String val = artifact.getMetadata().get(SCM_IDS);
+ if ( val != null ) {
+ final List<ArtifactId> result = new ArrayList<>();
+ for(final String v : val.split(",")) {
+ try {
+ final ArtifactId sourceId = ArtifactId.parse(v.trim());
+ if ( sourceId.getClassifier() == null ) {
+ throw new MojoExecutionException("Metadata " + SCM_IDS + " must specify classifier for source artifacts : " + sourceId.toMvnId());
+ }
+ result.add(sourceId);
+ } catch (final IllegalArgumentException iae) {
+ throw new MojoExecutionException("Wrong format for artifact id : " + v);
+ }
+ }
+ return result;
+ }
+ return null;
+ }
+
+ public static List<ArtifactId> getApiIds(final Artifact artifact) throws MojoExecutionException {
+ final String val = artifact.getMetadata().get(API_IDS);
+ if ( val != null ) {
+ final List<ArtifactId> result = new ArrayList<>();
+ for(final String v : val.split(",")) {
+ try {
+ final ArtifactId id = ArtifactId.parse(v.trim());
+ result.add(id);
+ } catch (final IllegalArgumentException iae) {
+ throw new MojoExecutionException("Wrong format for artifact id : " + v);
+ }
+ }
+ return result;
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/JavadocExecutor.java b/src/main/java/org/apache/sling/feature/maven/mojos/apis/JavadocExecutor.java
similarity index 98%
rename from src/main/java/org/apache/sling/feature/maven/mojos/JavadocExecutor.java
rename to src/main/java/org/apache/sling/feature/maven/mojos/apis/JavadocExecutor.java
index 2cfba55..a9f4b2d 100644
--- a/src/main/java/org/apache/sling/feature/maven/mojos/JavadocExecutor.java
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/apis/JavadocExecutor.java
@@ -14,7 +14,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.sling.feature.maven.mojos;
+package org.apache.sling.feature.maven.mojos.apis;
import java.io.File;
import java.io.FileNotFoundException;
@@ -31,7 +31,7 @@ import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.util.StringUtils;
-final class JavadocExecutor {
+public final class JavadocExecutor {
private static final char QUOTE_CHAR = '"';