blob: b8d79ae5bb71487c6d6071daeb618308c1bb49e8 [file] [log] [blame]
/*
* 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.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;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
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.Feature;
/**
* Context for creating the api jars
*/
public class ApisJarContext {
/**
* Information about a single artifact (bundle) taking part in the api generation.
*/
public static final class ArtifactInfo {
private Artifact artifact;
private File binDirectory;
private File sourceDirectory;
/** Exported packages used by all regions. */
private Set<String> usedExportedPackages;
/** Exported packages per region. */
private final Map<String, Set<Clause>> usedExportedPackagesRegion = new HashMap<>();
/** Flag if used as dependency */
private final Map<String, String> useAsDependencyPerRegion = new HashMap<>();
private final Set<File> includedResources = new HashSet<>();
private final Set<String> nodeTypes = new HashSet<>();
private List<License> licenses;
private final Set<String> sources = new HashSet<>();
public ArtifactInfo(final Artifact artifact) {
this.artifact = artifact;
}
public ArtifactId getId() {
return this.artifact.getId();
}
public Artifact getArtifact() {
return this.artifact;
}
public File getBinDirectory() {
return binDirectory;
}
public void setBinDirectory(File binDirectory) {
this.binDirectory = binDirectory;
}
public File getSourceDirectory() {
return sourceDirectory;
}
public void setSourceDirectory(File sourceDirectory) {
this.sourceDirectory = sourceDirectory;
}
public Set<String> getUsedExportedPackages() {
return usedExportedPackages;
}
public void setUsedExportedPackages(Set<String> usedExportedPackages) {
this.usedExportedPackages = usedExportedPackages;
}
public String[] getUsedExportedPackageIncludes() {
final Set<String> includes = new HashSet<>();
for(final String pck : usedExportedPackages) {
includes.add(pck.replace('.', '/').concat("/*"));
}
return includes.toArray(new String[includes.size()]);
}
public Set<Clause> getUsedExportedPackages(final String regionName) {
return this.usedExportedPackagesRegion.get(regionName);
}
public void setUsedExportedPackages(final String regionName, final Set<Clause> usedExportedPackages, final String useAsDependency) {
this.usedExportedPackagesRegion.put(regionName, usedExportedPackages);
if ( useAsDependency != null ) {
this.useAsDependencyPerRegion.put(regionName, useAsDependency);
}
}
public String[] getUsedExportedPackageIncludes(final String regionName) {
final Set<Clause> clauses = this.getUsedExportedPackages(regionName);
final Set<String> includes = new HashSet<>();
for(final Clause clause : clauses) {
includes.add(clause.getName().replace('.', '/').concat("/*"));
}
return includes.toArray(new String[includes.size()]);
}
public boolean isUseAsDependencyPerRegion(final String regionName) {
return this.useAsDependencyPerRegion.get(regionName) == null;
}
public String getNotUseAsDependencyPerRegionReason(final String regionName) {
return this.useAsDependencyPerRegion.get(regionName);
}
public Set<File> getIncludedResources() {
return includedResources;
}
/**
* Get all node types from this artifact
* @return The set of node types, might be empty
*/
public Set<String> getNodeTypes() {
return this.nodeTypes;
}
public List<License> getLicenses() {
return licenses;
}
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 If an incorrect configuration is found
*/
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;
}
public void addSourceInfo(final ArtifactId id) {
if ( id != null ) {
this.sources.add(id.toMvnId());
}
}
public void addSourceInfo(final String connection) {
if ( connection != null ) {
this.sources.add(connection);
}
}
public Set<String> getSources() {
return this.sources;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hash(artifact);
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof ArtifactInfo))
return false;
ArtifactInfo other = (ArtifactInfo) obj;
return Objects.equals(artifact, other.artifact);
}
}
private final ApisConfiguration config;
private final Map<ArtifactId, String> javadocClasspath = new HashMap<>();
private final Set<String> packagesWithoutJavaClasses = new HashSet<>();
private final Set<String> packagesWithoutSources = new HashSet<>();
private final File deflatedBinDir;
private final File deflatedSourcesDir;
private final File checkedOutSourcesDir;
private File javadocDir;
private final List<ArtifactInfo> infos = new ArrayList<>();
private final Feature feature;
private final Map<ArtifactId, Model> modelCache = new HashMap<>();
public ApisJarContext(final File mainDir, final Feature feature) throws MojoExecutionException {
this.config = new ApisConfiguration(feature);
this.feature = feature;
// deflated and source dirs can be shared
this.deflatedBinDir = new File(mainDir, "deflated-bin");
this.deflatedSourcesDir = new File(mainDir, "deflated-sources");
this.checkedOutSourcesDir = new File(mainDir, "checkouts");
}
public ApisConfiguration getConfig() {
return this.config;
}
public ArtifactId getFeatureId() {
return feature.getId();
}
public Feature getFeature() {
return this.feature;
}
public File getDeflatedBinDir() {
return deflatedBinDir;
}
public File getDeflatedSourcesDir() {
return deflatedSourcesDir;
}
public File getCheckedOutSourcesDir() {
return checkedOutSourcesDir;
}
public void addJavadocClasspath(final ArtifactId artifactId, final String classpath) {
javadocClasspath.put(artifactId, classpath);
}
public Map<ArtifactId, String> getJavadocClasspath() {
return javadocClasspath;
}
public File getJavadocDir() {
return javadocDir;
}
public void setJavadocDir(final File javadocDir) {
this.javadocDir = javadocDir;
}
public Set<String> getPackagesWithoutJavaClasses() {
return packagesWithoutJavaClasses;
}
public Set<String> getPackagesWithoutSources() {
return packagesWithoutSources;
}
public ArtifactInfo addArtifactInfo(final Artifact artifact) {
final ArtifactInfo info = new ArtifactInfo(artifact);
this.infos.add(info);
return info;
}
public ArtifactInfo getArtifactInfo(final ArtifactId artifactId) {
for(final ArtifactInfo i : this.infos) {
if ( i.getArtifact().getId().equals(artifactId)) {
return i;
}
}
return null;
}
public List<ArtifactInfo> getArtifactInfos() {
return this.infos;
}
public Map<ArtifactId, Model> getModelCache() {
return this.modelCache;
}
public Collection<ArtifactInfo> getArtifactInfos(final String regionName, final boolean omitDependencyArtifacts) {
final Map<ArtifactId, ArtifactInfo> result = new TreeMap<>();
for(final ArtifactInfo info : this.infos) {
final Set<Clause> pcks = info.getUsedExportedPackages(regionName);
if ( pcks != null && !pcks.isEmpty() ) {
if ( !omitDependencyArtifacts || !info.isUseAsDependencyPerRegion(regionName) ) {
result.put(info.getId(), info);
}
}
}
return result.values();
}
/**
* 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.getConfig().getDependencyRepositories().isEmpty() ) {
result = false;
log.debug("Trying to resolve ".concat(id.toMvnId()).concat(" from ").concat(this.getConfig().getDependencyRepositories().toString()));
for(final String server : this.getConfig().getDependencyRepositories()) {
try {
final URL url = new URL(server.concat(id.toMvnPath()));
try {
url.openConnection().getInputStream().close();
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 If an incorrect configuration is found
*/
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;
}
}