Implementing ability to process single-bundle ONOS apps whose meta-data
is self-contained in the jar itself.
Change-Id: Ie80ce5e39fd236dbcd0499ac1afec2399c2ea3cc
diff --git a/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
index a8b5e0e..3d92eae 100644
--- a/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
+++ b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
@@ -94,11 +94,12 @@
private static final String NET_PERMISSIONS = "security.permissions.net-perm";
private static final String JAVA_PERMISSIONS = "security.permissions.java-perm";
+ private static final String JAR = ".jar";
private static final String OAR = ".oar";
- private static final String PNG = "png";
private static final String APP_XML = "app.xml";
private static final String APP_PNG = "app.png";
private static final String M2_PREFIX = "m2";
+ private static final String FEATURES_XML = "features.xml";
private static final String ROOT = "../";
private static final String M2_ROOT = "system/";
@@ -198,14 +199,21 @@
checkState(!appFile(desc.name(), APP_XML).exists(),
"Application %s already installed", desc.name());
+ boolean isSelfContainedJar = false;
+
if (plainXml) {
expandPlainApplication(cache, desc);
} else {
bis.reset();
- expandZippedApplication(bis, desc);
+ isSelfContainedJar = expandZippedApplication(bis, desc);
+
+ if (isSelfContainedJar) {
+ bis.reset();
+ stageSelfContainedJar(bis, desc);
+ }
bis.reset();
- saveApplication(bis, desc);
+ saveApplication(bis, desc, isSelfContainedJar);
}
installArtifacts(desc);
@@ -326,8 +334,10 @@
}
// Expands the specified ZIP stream into app-specific directory.
- private void expandZippedApplication(InputStream stream, ApplicationDescription desc)
+ // Returns true of the application is a self-contained jar rather than an oar file.
+ private boolean expandZippedApplication(InputStream stream, ApplicationDescription desc)
throws IOException {
+ boolean isSelfContained = false;
ZipInputStream zis = new ZipInputStream(stream);
ZipEntry entry;
File appDir = new File(appsDir, desc.name());
@@ -336,11 +346,67 @@
byte[] data = ByteStreams.toByteArray(zis);
zis.closeEntry();
File file = new File(appDir, entry.getName());
- createParentDirs(file);
- write(data, file);
+ if (isTopLevel(file)) {
+ createParentDirs(file);
+ write(data, file);
+ } else {
+ isSelfContained = true;
+ }
}
}
zis.close();
+ return isSelfContained;
+ }
+
+ // Returns true if the specified file is a top-level app file, i.e. app.xml,
+ // features.xml, .jar or a directory; false if anything else.
+ private boolean isTopLevel(File file) {
+ String name = file.getName();
+ return name.equals(APP_XML) || name.endsWith(FEATURES_XML) || name.endsWith(JAR) || file.isDirectory();
+ }
+
+ // Expands the self-contained JAR stream into the app-specific directory,
+ // using the bundle coordinates retrieved from the features.xml file.
+ private void stageSelfContainedJar(InputStream stream, ApplicationDescription desc)
+ throws IOException {
+ // First extract the bundle coordinates
+ String coords = getSelfContainedBundleCoordinates(desc);
+ if (coords == null) {
+ return;
+ }
+
+ // Split the coordinates into segments and build the file name.
+ String[] f = coords.substring(4).split("/");
+ String base = "m2/" + f[0].replace('.', '/') + "/" + f[1] + "/" + f[2] + "/" + f[1] + "-" + f[2];
+ String jarName = base + (f.length < 4 ? "" : "-" + f[3]) + ".jar";
+ String featuresName = base + "-features.xml";
+
+ // Create the file directory structure and copy the file there.
+ File jar = appFile(desc.name(), jarName);
+ boolean ok = jar.getParentFile().mkdirs();
+ if (ok) {
+ Files.write(toByteArray(stream), jar);
+ Files.copy(appFile(desc.name(), FEATURES_XML), appFile(desc.name(), featuresName));
+ if (!appFile(desc.name(), FEATURES_XML).delete()) {
+ log.warn("Unable to delete self-contained application {} features.xml", desc.name());
+ }
+ } else {
+ throw new IOException("Unable to save self-contained application " + desc.name());
+ }
+ }
+
+ // Returns the bundle coordinates from the features.xml file.
+ private String getSelfContainedBundleCoordinates(ApplicationDescription desc) {
+ try {
+ XMLConfiguration cfg = new XMLConfiguration();
+ cfg.setAttributeSplittingDisabled(true);
+ cfg.setDelimiterParsingDisabled(true);
+ cfg.load(appFile(desc.name(), FEATURES_XML));
+ return cfg.getString("feature.bundle");
+ } catch (ConfigurationException e) {
+ log.warn("Self-contained application {} has no features.xml", desc.name());
+ return null;
+ }
}
// Saves the specified XML stream into app-specific directory.
@@ -352,11 +418,12 @@
write(stream, file);
}
-
// Saves the specified ZIP stream into a file under app-specific directory.
- private void saveApplication(InputStream stream, ApplicationDescription desc)
+ private void saveApplication(InputStream stream, ApplicationDescription desc,
+ boolean isSelfContainedJar)
throws IOException {
- Files.write(toByteArray(stream), appFile(desc.name(), desc.name() + OAR));
+ String name = desc.name() + (isSelfContainedJar ? JAR : OAR);
+ Files.write(toByteArray(stream), appFile(desc.name(), name));
}
// Installs application artifacts into M2 repository.