/*
* 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.
*
* Modified for usage within IntelliJ IDEA.
*/
package org.osmorc.maven.facet;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Constants;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.idea.maven.model.MavenArtifact;
import java.io.File;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* Add BND directives to embed selected dependencies inside a bundle
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
* @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a>
*/
public final class DependencyEmbedder extends AbstractDependencyFilter {
public static final String EMBED_DEPENDENCY = "Embed-Dependency";
public static final String EMBED_DIRECTORY = "Embed-Directory";
public static final String EMBED_STRIP_GROUP = "Embed-StripGroup";
public static final String EMBED_STRIP_VERSION = "Embed-StripVersion";
public static final String EMBED_TRANSITIVE = "Embed-Transitive";
public static final String EMBEDDED_ARTIFACTS = "Embedded-Artifacts";
public static final String MAVEN_DEPENDENCIES = "{maven-dependencies}";
private String myEmbedDirectory;
private String myEmbedStripGroup;
private String myEmbedStripVersion;
private final LinkedHashSet<String> myInlinePaths;
private final Collection<MavenArtifact> myEmbeddedArtifacts;
public DependencyEmbedder(Collection<MavenArtifact> dependencyArtifacts) {
super(dependencyArtifacts);
myInlinePaths = new LinkedHashSet<>();
myEmbeddedArtifacts = new LinkedHashSet<>();
}
public void processHeaders(Analyzer analyzer) throws DependencyEmbedderException {
StringBuilder includeResource = new StringBuilder();
StringBuilder bundleClassPath = new StringBuilder();
StringBuilder embeddedArtifacts = new StringBuilder();
myInlinePaths.clear();
myEmbeddedArtifacts.clear();
String embedDependencyHeader = analyzer.getProperty(EMBED_DEPENDENCY);
if (StringUtil.isNotEmpty(embedDependencyHeader)) {
myEmbedDirectory = analyzer.getProperty(EMBED_DIRECTORY);
myEmbedStripGroup = analyzer.getProperty(EMBED_STRIP_GROUP, "true");
myEmbedStripVersion = analyzer.getProperty(EMBED_STRIP_VERSION);
processInstructions(embedDependencyHeader);
for (String inlinePath : myInlinePaths) {
inlineDependency(inlinePath, includeResource);
}
for (MavenArtifact embeddedArtifact : myEmbeddedArtifacts) {
embedDependency(embeddedArtifact, includeResource, bundleClassPath, embeddedArtifacts);
}
}
if (analyzer.getProperty(Constants.WAB) == null && bundleClassPath.length() > 0) {
// set explicit default before merging dependency classpath
if (analyzer.getProperty(Constants.BUNDLE_CLASSPATH) == null) {
analyzer.setProperty(Constants.BUNDLE_CLASSPATH, ".");
}
}
appendDependencies(analyzer, Constants.INCLUDE_RESOURCE, includeResource.toString());
appendDependencies(analyzer, Constants.BUNDLE_CLASSPATH, bundleClassPath.toString());
appendDependencies(analyzer, EMBEDDED_ARTIFACTS, embeddedArtifacts.toString());
}
@Override
protected void processDependencies(Collection<MavenArtifact> dependencies, String inline) {
if (null == inline || "false".equalsIgnoreCase(inline)) {
myEmbeddedArtifacts.addAll(dependencies);
}
else {
for (Object dependency : dependencies) {
addInlinePaths((MavenArtifact)dependency, inline, myInlinePaths);
}
}
}
private static void addInlinePaths(MavenArtifact dependency, String inline, Collection<String> inlinePaths) {
File path = dependency.getFile();
if (path.exists()) {
if ("true".equalsIgnoreCase(inline) || inline.length() == 0) {
inlinePaths.add(path.getPath());
}
else {
String[] filters = inline.split("\\|");
for (String filter : filters) {
if (filter.length() > 0) {
inlinePaths.add(path + "!/" + filter);
}
}
}
}
}
private void embedDependency(MavenArtifact dependency, StringBuilder includeResource, StringBuilder bundleClassPath,
StringBuilder embeddedArtifacts) {
File sourceFile = dependency.getFile();
if (sourceFile.exists()) {
String embedDirectory = myEmbedDirectory;
if (myEmbedDirectory == null || embedDirectory.isEmpty() || ".".equals(embedDirectory)) {
embedDirectory = null;
}
if (!Boolean.valueOf(myEmbedStripGroup).booleanValue()) {
embedDirectory = new File(embedDirectory, dependency.getGroupId()).getPath();
}
StringBuilder targetFileName = new StringBuilder();
targetFileName.append(dependency.getArtifactId());
if (!Boolean.valueOf(myEmbedStripVersion).booleanValue()) {
targetFileName.append('-').append(dependency.getVersion());
if (StringUtil.isNotEmpty(dependency.getClassifier())) {
targetFileName.append('-').append(dependency.getClassifier());
}
}
String extension = dependency.getExtension();
if (StringUtil.isNotEmpty(extension)) {
targetFileName.append('.').append(extension);
}
File targetFile = new File(embedDirectory, targetFileName.toString());
String targetFilePath = targetFile.getPath();
// replace windows backslash with a slash
if (File.separatorChar != '/') {
targetFilePath = targetFilePath.replace(File.separatorChar, '/');
}
if (includeResource.length() > 0) {
includeResource.append(',');
}
includeResource.append(targetFilePath);
includeResource.append('=');
includeResource.append(sourceFile);
if (bundleClassPath.length() > 0) {
bundleClassPath.append(',');
}
bundleClassPath.append(targetFilePath);
if (embeddedArtifacts.length() > 0) {
embeddedArtifacts.append(',');
}
embeddedArtifacts.append(targetFilePath).append(';');
embeddedArtifacts.append("g=\"").append(dependency.getGroupId()).append('"');
embeddedArtifacts.append(";a=\"").append(dependency.getArtifactId()).append('"');
embeddedArtifacts.append(";v=\"").append(dependency.getVersion()).append('"');
if (StringUtil.isNotEmpty(dependency.getClassifier())) {
embeddedArtifacts.append(";c=\"").append(dependency.getClassifier()).append('"');
}
}
}
private static void inlineDependency(String path, StringBuilder includeResource) {
if (includeResource.length() > 0) {
includeResource.append(',');
}
includeResource.append('@');
includeResource.append(path);
}
private static void appendDependencies(Analyzer analyzer, String directiveName, String mavenDependencies) {
// similar algorithm to {maven-resources} but default behaviour here is to append rather than override
final String instruction = analyzer.getProperty(directiveName);
if (StringUtil.isNotEmpty(instruction)) {
if (instruction.contains(MAVEN_DEPENDENCIES)) {
// if there are no embedded dependencies, we do a special treatment and replace
// every occurrence of MAVEN_DEPENDENCIES and a following comma with an empty string
if (mavenDependencies.length() == 0) {
String cleanInstruction = ImporterUtil.removeTagFromInstruction(instruction, MAVEN_DEPENDENCIES);
analyzer.setProperty(directiveName, cleanInstruction);
}
else {
String mergedInstruction = StringUtil.replace(instruction, MAVEN_DEPENDENCIES, mavenDependencies);
analyzer.setProperty(directiveName, mergedInstruction);
}
}
else if (mavenDependencies.length() > 0) {
if (Constants.INCLUDE_RESOURCE.equalsIgnoreCase(directiveName)) {
// dependencies should be prepended so they can be overwritten by local resources
analyzer.setProperty(directiveName, mavenDependencies + ',' + instruction);
}
else
// Analyzer.BUNDLE_CLASSPATH
{
// for the classpath we want dependencies to be appended after local entries
analyzer.setProperty(directiveName, instruction + ',' + mavenDependencies);
}
}
// otherwise leave instruction unchanged
}
else if (mavenDependencies.length() > 0) {
analyzer.setProperty(directiveName, mavenDependencies);
}
// otherwise leave instruction unchanged
}
}