Adding src/main/webapp resources to WARs
This fixes onos-gui, so now onos-rest and onos-gui work.
We also exclude duplicate resources in the output jar.
Change-Id: I5fef1376a9f7e88cb7248a606e8f568f641ab45b
diff --git a/utils/osgiwrap/src/main/java/org/onlab/osgiwrap/OSGiWrapper.java b/utils/osgiwrap/src/main/java/org/onlab/osgiwrap/OSGiWrapper.java
index 69d61df..6416de1 100644
--- a/utils/osgiwrap/src/main/java/org/onlab/osgiwrap/OSGiWrapper.java
+++ b/utils/osgiwrap/src/main/java/org/onlab/osgiwrap/OSGiWrapper.java
@@ -16,16 +16,28 @@
package org.onlab.osgiwrap;
+import aQute.bnd.header.Attrs;
+import aQute.bnd.header.Parameters;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Builder;
+import aQute.bnd.osgi.FileResource;
import aQute.bnd.osgi.Jar;
+import aQute.bnd.osgi.Resource;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.felix.scrplugin.bnd.SCRDescriptorBndPlugin;
import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -34,10 +46,13 @@
import java.util.Set;
import java.util.jar.Manifest;
+import static java.nio.file.Files.walkFileTree;
+
/**
* BND-based wrapper to convert Buck JARs to OSGi-compatible JARs.
*/
public class OSGiWrapper {
+ private static final String NONE = "NONE";
private String inputJar;
private String outputJar;
@@ -48,14 +63,18 @@
private String bundleSymbolicName;
private String bundleVersion;
+ private String importPackages;
+ private String exportPackages;
+ private String includeResources;
+ private Set<String> includedResources = Sets.newHashSet();
+
private String bundleDescription;
private String bundleLicense;
private String webContext;
public static void main(String[] args) {
-
- if (args.length < 7) {
+ if (args.length < 10) {
System.err.println("Not enough args");
System.exit(1);
}
@@ -67,16 +86,21 @@
String group = args[4];
String version = args[5];
String license = args[6];
- String webContext = args[7];
- String desc = Joiner.on(' ').join(Arrays.copyOfRange(args, 8, args.length));
+ String importPackages = args[7];
+ String exportPackages = args[8];
+ String includeResources = args[9];
+ String webContext = args[10];
+ String desc = Joiner.on(' ').join(Arrays.copyOfRange(args, 11, args.length));
OSGiWrapper wrapper = new OSGiWrapper(jar, output, cp,
name, group,
version, license,
+ importPackages, exportPackages,
+ includeResources,
webContext, desc);
wrapper.log(wrapper + "\n");
if (!wrapper.execute()) {
- System.err.println("ERROR");
+ System.err.printf("Error generating %s\n", name);
System.exit(2);
}
}
@@ -89,6 +113,9 @@
String groupId,
String bundleVersion,
String bundleLicense,
+ String importPackages,
+ String exportPackages,
+ String includeResources,
String webContext,
String bundleDescription) {
this.inputJar = inputJar;
@@ -106,6 +133,12 @@
this.bundleLicense = bundleLicense;
this.bundleDescription = bundleDescription;
+ this.importPackages = importPackages;
+ this.exportPackages = exportPackages;
+ if (!Objects.equals(includeResources, NONE)) {
+ this.includeResources = includeResources;
+ }
+
this.webContext = webContext;
}
@@ -122,17 +155,20 @@
//analyzer.setProperty("-consumer-policy", "${range;[===,==+)}");
// There are no good defaults so make sure you set the Import-Package
- analyzer.setProperty(Analyzer.IMPORT_PACKAGE, "*");
+ analyzer.setProperty(Analyzer.IMPORT_PACKAGE, importPackages);
// TODO include version in export, but not in import
- analyzer.setProperty(Analyzer.EXPORT_PACKAGE, "*");
+ analyzer.setProperty(Analyzer.EXPORT_PACKAGE, exportPackages);
// TODO we may need INCLUDE_RESOURCE, or that might be done by Buck
- //analyzer.setProperty(analyzer.INCLUDE_RESOURCE, ...)
+ if (includeResources != null) {
+ analyzer.setProperty(Analyzer.INCLUDE_RESOURCE, includeResources);
+ }
+
if (isWab()) {
analyzer.setProperty(Analyzer.WAB, "src/main/webapp/");
analyzer.setProperty("Web-ContextPath", webContext);
- analyzer.setProperty(Analyzer.IMPORT_PACKAGE, "*,org.glassfish.jersey.servlet");
+ analyzer.setProperty(Analyzer.IMPORT_PACKAGE, "*,org.glassfish.jersey.servlet,org.jvnet.mimepull\n");
}
}
@@ -165,6 +201,10 @@
scrDescriptorBndPlugin.setReporter(analyzer);
scrDescriptorBndPlugin.analyzeJar(analyzer);
+ if (includeResources != null) {
+ doIncludeResources(analyzer);
+ }
+
// Repack the JAR as a WAR
doWabStaging(analyzer);
@@ -176,10 +216,14 @@
if (analyzer.isOk()) {
analyzer.getJar().setManifest(manifest);
- analyzer.save(new File(outputJar), true);
- log("Saved!\n");
+ if (analyzer.save(new File(outputJar), true)) {
+ log("Saved!\n");
+ } else {
+ warn("Failed to create jar \n");
+ return false;
+ }
} else {
- warn("%s\n", analyzer.getErrors());
+ warn("Analyzer Errors:\n%s\n", analyzer.getErrors());
return false;
}
@@ -193,7 +237,7 @@
}
private boolean isWab() {
- return !Objects.equals(webContext, "NONE");
+ return !Objects.equals(webContext, NONE);
}
private void doWabStaging(Analyzer analyzer) throws Exception {
@@ -215,6 +259,88 @@
dot.rename(path, "WEB-INF/classes/" + path);
}
}
+
+ Path wabRoot = Paths.get(wab);
+ includeFiles(dot, null, wabRoot.toString());
+ }
+
+ /**
+ * Parse the Bundle-Includes header. Files in the bundles Include header are
+ * included in the jar. The source can be a directory or a file.
+ *
+ * @throws Exception
+ */
+ private void doIncludeResources(Analyzer analyzer) throws Exception {
+ String includes = analyzer.getProperty(Analyzer.INCLUDE_RESOURCE);
+ if (includes == null) {
+ return;
+ }
+ Parameters clauses = analyzer.parseHeader(includes);
+ Jar jar = analyzer.getJar();
+
+ for (Map.Entry<String, Attrs> entry : clauses.entrySet()) {
+ String name = entry.getKey();
+ Map<String, String> extra = entry.getValue();
+ // TODO consider doing something with extras
+
+ String[] parts = name.split("\\s*=\\s*");
+ String source = parts[0];
+ String destination = parts[0];
+ if (parts.length == 2) {
+ source = parts[1];
+ }
+
+ includeFiles(jar, destination, source);
+ }
+ }
+
+ private void includeFiles(Jar jar, String destinationRoot, String sourceRoot)
+ throws IOException {
+ Path sourceRootPath = Paths.get(sourceRoot);
+ // iterate through sources
+ // put each source on the jar
+ FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Path relativePath = sourceRootPath.relativize(file);
+ String destination = destinationRoot != null ?
+ destinationRoot + "/" + relativePath.toString() : //TODO
+ relativePath.toString();
+
+ addFileToJar(jar, destination, file.toAbsolutePath().toString());
+ return FileVisitResult.CONTINUE;
+ }
+ };
+ File dir = new File(sourceRoot);
+ if (dir.isFile()) {
+ addFileToJar(jar, destinationRoot, dir.getAbsolutePath());
+ } else if (dir.isDirectory()) {
+ walkFileTree(sourceRootPath, visitor);
+ } else {
+ warn("Skipping resource in bundle %s: %s (File Not Found)\n",
+ bundleSymbolicName, sourceRoot);
+ }
+ }
+
+ private boolean addFileToJar(Jar jar, String destination, String sourceAbsPath) {
+ if (includedResources.contains(sourceAbsPath)) {
+ log("Skipping already included resource: %s\n", sourceAbsPath);
+ return false;
+ }
+ File file = new File(sourceAbsPath);
+ if (!file.isFile()) {
+ throw new RuntimeException(
+ String.format("Skipping non-existent file: %s\n", sourceAbsPath));
+ }
+ Resource resource = new FileResource(file);
+ if (jar.getResource(destination) != null) {
+ warn("Skipping duplicate resource: %s\n", destination);
+ return false;
+ }
+ jar.putResource(destination, resource);
+ includedResources.add(sourceAbsPath);
+ log("Adding resource: %s\n", destination);
+ return true;
}
private void log(String format, Object... objects) {