
com.commsen.em.maven.plugins.BndExportPlugin Maven / Gradle / Ivy
package com.commsen.em.maven.plugins;
import static com.commsen.em.maven.extension.Constants.DEFAULT_TMP_BUNDLES;
import static com.commsen.em.maven.extension.Constants.PROP_ACTION_RESOLVE;
import static com.commsen.em.maven.extension.Constants.PROP_CONFIG_INDEX;
import static com.commsen.em.maven.extension.Constants.PROP_CONFIG_TMP_BUNDLES;
import static com.commsen.em.maven.extension.Constants.PROP_CONTRACTORS;
import static com.commsen.em.maven.extension.Constants.PROP_CONTRACTS;
import static com.commsen.em.maven.extension.Constants.INTERNAL_DISTRO_FILE;
import static com.commsen.em.maven.extension.Constants.PROP_RESOLVE_OUTPUT;
import static com.commsen.em.maven.extension.Constants.PROP_EXECUTABLE_RUN_PROPERTIES;
import static com.commsen.em.maven.extension.Constants.VAL_EXTENSION_VERSION;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.ops4j.pax.swissbox.bnd.BndUtils;
import org.ops4j.pax.swissbox.bnd.OverwriteMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.commsen.em.maven.util.Dependencies;
import com.commsen.em.maven.util.Flag;
import com.commsen.em.maven.util.Templates;
import com.commsen.em.maven.util.Version;
import freemarker.template.TemplateException;
@Component(role = BndExportPlugin.class)
public class BndExportPlugin extends DynamicMavenPlugin {
private Logger logger = LoggerFactory.getLogger(BndExportPlugin.class);
@Requirement
private Dependencies dependencies;
@Requirement
private Templates templates;
private List filesToCleanup = new LinkedList<>();
public void addToPomForExport(MavenProject project) throws MavenExecutionException {
String bndrunName = getBndrunName(project);
addToPom(project, true, bndrunName);
project.getProperties().setProperty(PROP_RESOLVE_OUTPUT, "${project.build.directory}/export/" + bndrunName);
logger.info("Added `bnd-export-maven-plugin` to genrate list of modules needed at runtume!");
}
public void addToPomForExecutable(MavenProject project) throws MavenExecutionException {
addToPom(project, false, getBndrunName(project));
logger.info("Added `bnd-export-maven-plugin` to genrate list of modules incuded in executable jar!");
}
public void addToPom(MavenProject project, boolean bundlesOnly, String bndrun) throws MavenExecutionException {
Set bundles = prepareDependencies(project);
File thisArtifact = new File(project.getBuild().getDirectory(),
project.getBuild().getFinalName() + "." + project.getPackaging());
bundles.add(thisArtifact);
Map model = new HashMap();
model.put("bundlesOnly", bundlesOnly);
model.put("bndrun", bndrun);
model.put("bundles", bundles);
String configuration = null;
try {
configuration = templates.process("META-INF/templates/bnd-export-maven-plugin-configuration.fmt", model);
} catch (IOException | TemplateException e) {
throw new MavenExecutionException("Failed to process template file!", e);
}
Plugin plugin = createPlugin("com.commsen.em", "em-maven-plugin", VAL_EXTENSION_VERSION, configuration, "export", "export", "package");
// Plugin plugin = createPlugin("biz.aQute.bnd", "bnd-export-maven-plugin", VAL_BND_VERSION, configuration,
// "export", "export", "package");
project.getBuild().getPlugins().add(0, plugin);
createBndrunIfNotExists(project, bndrun);
}
public void cleanup() {
for (File file : filesToCleanup) {
FileUtils.deleteQuietly(file);
}
}
private String getBndrunName(MavenProject project) throws MavenExecutionException {
String bndrunName = project.getProperties().getProperty(PROP_ACTION_RESOLVE + ".bndrun", "");
if (bndrunName.trim().isEmpty()) {
bndrunName = project.getName();
}
if (bndrunName.endsWith(".bndrun")) {
bndrunName = bndrunName.substring(0, bndrunName.length() - ".bndrun".length());
}
int count = 0;
while (new File(project.getBasedir(), bndrunName + ".bndrun").isFile()) {
bndrunName = bndrunName + "." + ++count;
}
return bndrunName;
}
private void createBndrunIfNotExists(MavenProject project, String bndrunName) throws MavenExecutionException {
if (bndrunName == null || bndrunName.trim().isEmpty()) {
throw new MavenExecutionException("Invalid bndrun name: " + bndrunName, project.getFile());
}
File bndrunFile = new File(project.getBasedir(), bndrunName + ".bndrun");
if (bndrunFile.exists() && !bndrunFile.isFile()) {
throw new MavenExecutionException("'" + bndrunFile + "' is not a file!", project.getFile());
} else {
String distro = project.getProperties().getProperty(INTERNAL_DISTRO_FILE, "");
generateBndrun(project, distro, bndrunFile);
filesToCleanup.add(bndrunFile);
}
}
private void generateBndrun(MavenProject project, String distro, File bndFile) throws MavenExecutionException {
Set requirements = new HashSet<>();
requirements.add("osgi.identity;filter:='(osgi.identity=" + project.getArtifactId() + ")'");
String contracts = project.getProperties().getProperty(PROP_CONTRACTS);
if (contracts != null && !contracts.trim().isEmpty()) {
String[] contractsArray = contracts.split("[\\s]*,[\\s]*");
for (String contract : contractsArray) {
requirements.add("em.contract;filter:='(em.contract=" + contract + ")';effective:=assemble");
}
}
Set runProperties = new HashSet<>();
String runPropertiesText = project.getProperties().getProperty(PROP_EXECUTABLE_RUN_PROPERTIES);
if (runPropertiesText != null && !runPropertiesText.trim().isEmpty()) {
String[] propertiesArray = runPropertiesText.split("[\\s]*\\n[\\s]*");
runProperties.addAll(Arrays.asList(propertiesArray));
}
Map model = new HashMap<>();
model.put("requirements", requirements);
model.put("runProperties", runProperties);
model.put("distro", distro);
String bndrunContent = null;
try {
bndrunContent = templates.process("META-INF/templates/bndrun.fmt", model);
} catch (IOException | TemplateException e) {
throw new MavenExecutionException("Failed to process template file!", e);
}
if (logger.isDebugEnabled())
logger.debug("Generated bndrun file: \n{}", bndrunContent);
writeBndrun(bndFile, bndrunContent);
if (Flag.keepBndrun()) {
writeBndrun(new File(project.getBasedir(), "_em.generated.bndrun"), bndrunContent);
}
}
private void writeBndrun(File generatedBndrunFile, String content) throws MavenExecutionException {
try {
generatedBndrunFile.createNewFile();
generatedBndrunFile.deleteOnExit();
PrintWriter writer = new PrintWriter(generatedBndrunFile, "UTF-8");
writer.print(content.replaceAll("\\t", " "));
writer.close();
} catch (IOException e) {
throw new MavenExecutionException("Failed to create temprary bndrun file!", e);
}
}
private Set prepareDependencies(MavenProject project) {
if (logger.isDebugEnabled()) {
logger.debug("Analysing project's BOMs!");
} else if (Flag.verbose()) {
logger.info("Analysing project's BOMs!");
}
File tmpBundleFolder = new File(project.getBasedir(),
project.getProperties().getProperty(PROP_CONFIG_TMP_BUNDLES, DEFAULT_TMP_BUNDLES));
try {
FileUtils.forceMkdir(tmpBundleFolder);
} catch (IOException e) {
throw new RuntimeException("Failed to create temprary directory: " + tmpBundleFolder, e);
}
filesToCleanup.add(tmpBundleFolder);
Set bundleSet = new HashSet();
boolean generateIndex = project.getProperties().containsKey(PROP_CONFIG_INDEX);
processModules(project);
Set artifacts = new HashSet<>();
try {
artifacts.addAll(dependencies.asArtifacts(project));
artifacts.addAll(dependencies.managedAsArtifacts(project));
} catch (MavenExecutionException e) {
throw new RuntimeException("Failed to analyze dependencies", e);
}
/*
* For each artifact : - convert to bundle if it's not - add it to the bundle
* set
*/
artifacts.stream().forEach(a -> addToBundleSet(a, tmpBundleFolder, bundleSet, generateIndex));
return bundleSet;
}
public void processModules(MavenProject project) {
String modulesText = project.getProperties().getProperty(PROP_CONTRACTORS);
if (modulesText == null || modulesText.trim().isEmpty()) {
if (Flag.verbose()) {
logger.info("No available modules in '{}'", PROP_CONTRACTORS);
} else if (logger.isDebugEnabled()) {
logger.debug("No available modules in '{}'", PROP_CONTRACTORS);
}
return;
}
String[] modulesArray = modulesText.split("[\\s]*[,\\n][\\s]*");
Set modulesSet = Arrays.stream(modulesArray).collect(Collectors.toSet());
modulesSet.add("com.commsen.em.contractors:em.contractors.runtime:" + VAL_EXTENSION_VERSION);
for (String moduleText : modulesSet) {
String[] coordinates = moduleText.split(":");
if (coordinates.length != 3) {
logger.warn("Invalid maven coordinates for module '{}'! It will be ignored!", moduleText);
continue;
}
Dependency dependency = new Dependency();
dependency.setGroupId(coordinates[0]);
dependency.setArtifactId(coordinates[1]);
dependency.setVersion(coordinates[2]);
dependency.setScope("runtime");
dependency.setType("pom");
try {
Artifact pomArtifact = dependencies.asArtifact(project, dependency);
dependency.setType("jar");
MavenXpp3Reader reader = new MavenXpp3Reader();
Model model = reader.read(new FileInputStream(pomArtifact.getFile()));
DependencyManagement dm = model.getDependencyManagement();
if (dm == null) {
dependencies.addToDependencyManagement(project, dependency);
} else {
for (Dependency d : dm.getDependencies()) {
/*
* TODO handle variables properly! For now assume variable is referring to the
* contract's artifact itself (that's what EM contractors do).
*/
if (d.getArtifactId().startsWith("${")) {
dependencies.addToDependencyManagement(project, dependency);
} else {
dependencies.addToDependencyManagement(project, d);
}
}
}
} catch (Exception e) {
logger.warn("Could not process modules from " + coordinates[0] + ":" + coordinates[1] + ":"
+ coordinates[2], e);
}
}
}
/**
* @param artifact
* @param bundlesDirectory
* @param bundlesSet
* @param indexGeneration
*/
private void addToBundleSet(Artifact artifact, File bundlesDirectory, Set bundlesSet,
boolean indexGeneration) {
File f;
if (dependencies.isOSGiBundle(artifact)) {
f = artifact.getFile();
/*
* If index is to be created copy the bundles to the temporary folder
*/
if (indexGeneration) {
try {
FileUtils.copyFile(artifact.getFile(), new File(bundlesDirectory, f.getName()));
} catch (IOException e) {
throw new RuntimeException("Failed to copy file to temporary folder", e);
}
}
} else {
f = new File(bundlesDirectory, artifact.getArtifactId() + "-" + artifact.getVersion() + "-EM.jar");
makeBundle(artifact, f);
}
bundlesSet.add(f);
if (logger.isDebugEnabled()) {
logger.debug("Made '{}' module available to the resolver", artifact);
} else if (Flag.verbose()) {
logger.info("Made '{}' module available to the resolver", artifact);
}
}
private boolean makeBundle(Artifact artifact, File targetFile) {
Properties properties = new Properties();
properties.put("Bundle-SymbolicName", artifact.getArtifactId());
properties.put("Bundle-Version", Version.semantic(artifact.getVersion()));
properties.put("Original-Version", artifact.getVersion());
try (FileInputStream fileStream = new FileInputStream(artifact.getFile());
InputStream bndStream = BndUtils.createBundle(fileStream, properties, artifact.getFile().getName(),
OverwriteMode.FULL);
OutputStream outStream = new FileOutputStream(targetFile);) {
byte[] buffer = new byte[8 * 1024];
int bytesRead;
while ((bytesRead = bndStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
if (logger.isDebugEnabled()) {
logger.debug("'{}' wrapped in module '{}'", artifact.getFile(), targetFile);
} else if (Flag.verbose()) {
logger.info("'{}' wrapped in module '{}'", artifact.getFile(), targetFile);
}
return true;
} catch (IOException e) {
logger.warn("Failed to convert '{}' to module ", artifact, e);
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy