Added ability to upload apps as both app.xml or app.zip.
Added a number of app.xml files for built-in apps.
Added ability to install & activate in one command.
Change-Id: I3fa5fa487ef76d9fe3da4d6dce8045d538cba423
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 0260863..5a818e4 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
@@ -40,6 +40,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
+import java.nio.charset.Charset;
import java.nio.file.NoSuchFileException;
import java.util.List;
import java.util.Set;
@@ -57,6 +58,13 @@
public class ApplicationArchive
extends AbstractStore<ApplicationEvent, ApplicationStoreDelegate> {
+ // Magic strings to search for at the beginning of the archive stream
+ private static final String XML_MAGIC = "<?xml ";
+
+ // Magic strings to search for and how deep to search it into the archive stream
+ private static final String APP_MAGIC = "<app ";
+ private static final int APP_MAGIC_DEPTH = 1024;
+
private static final String NAME = "[@name]";
private static final String ORIGIN = "[@origin]";
private static final String VERSION = "[@version]";
@@ -144,13 +152,21 @@
try (InputStream ais = stream) {
byte[] cache = toByteArray(ais);
InputStream bis = new ByteArrayInputStream(cache);
- ApplicationDescription desc = parseAppDescription(bis);
- bis.reset();
- expandApplication(bis, desc);
- bis.reset();
+ boolean plainXml = isPlainXml(cache);
+ ApplicationDescription desc = plainXml ?
+ parsePlainAppDescription(bis) : parseZippedAppDescription(bis);
- saveApplication(bis, desc);
+ if (plainXml) {
+ expandPlainApplication(cache, desc);
+ } else {
+ bis.reset();
+ expandZippedApplication(bis, desc);
+
+ bis.reset();
+ saveApplication(bis, desc);
+ }
+
installArtifacts(desc);
return desc;
} catch (IOException e) {
@@ -158,28 +174,45 @@
}
}
+ // Indicates whether the stream encoded in the given bytes is plain XML.
+ private boolean isPlainXml(byte[] bytes) {
+ return substring(bytes, XML_MAGIC.length()).equals(XML_MAGIC) ||
+ substring(bytes, APP_MAGIC_DEPTH).contains(APP_MAGIC);
+ }
+
+ // Returns the substring of maximum possible length from the specified bytes.
+ private String substring(byte[] bytes, int length) {
+ return new String(bytes, 0, Math.min(bytes.length, length), Charset.forName("UTF-8"));
+ }
+
/**
* Purges the application archive directory.
*
* @param appName application name
*/
public void purgeApplication(String appName) {
+ File appDir = new File(appsDir, appName);
try {
- Tools.removeDirectory(new File(appsDir, appName));
+ Tools.removeDirectory(appDir);
} catch (IOException e) {
throw new ApplicationException("Unable to purge application " + appName, e);
}
+ if (appDir.exists()) {
+ throw new ApplicationException("Unable to purge application " + appName);
+ }
}
/**
- * Returns application archive stream for the specified application.
+ * Returns application archive stream for the specified application. This
+ * will be either the application ZIP file or the application XML file.
*
* @param appName application name
* @return application archive stream
*/
public InputStream getApplicationInputStream(String appName) {
try {
- return new FileInputStream(appFile(appName, appName + ".zip"));
+ File appFile = appFile(appName, appName + ".zip");
+ return new FileInputStream(appFile.exists() ? appFile : appFile(appName, APP_XML));
} catch (FileNotFoundException e) {
throw new ApplicationException("Application " + appName + " not found");
}
@@ -187,20 +220,14 @@
// Scans the specified ZIP stream for app.xml entry and parses it producing
// an application descriptor.
- private ApplicationDescription parseAppDescription(InputStream stream)
+ private ApplicationDescription parseZippedAppDescription(InputStream stream)
throws IOException {
try (ZipInputStream zis = new ZipInputStream(stream)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().equals(APP_XML)) {
byte[] data = ByteStreams.toByteArray(zis);
- XMLConfiguration cfg = new XMLConfiguration();
- try {
- cfg.load(new ByteArrayInputStream(data));
- return loadAppDescription(cfg);
- } catch (ConfigurationException e) {
- throw new IOException("Unable to parse " + APP_XML, e);
- }
+ return parsePlainAppDescription(new ByteArrayInputStream(data));
}
zis.closeEntry();
}
@@ -208,6 +235,18 @@
throw new IOException("Unable to locate " + APP_XML);
}
+ // Scans the specified XML stream and parses it producing an application descriptor.
+ private ApplicationDescription parsePlainAppDescription(InputStream stream)
+ throws IOException {
+ XMLConfiguration cfg = new XMLConfiguration();
+ try {
+ cfg.load(stream);
+ return loadAppDescription(cfg);
+ } catch (ConfigurationException e) {
+ throw new IOException("Unable to parse " + APP_XML, e);
+ }
+ }
+
private ApplicationDescription loadAppDescription(XMLConfiguration cfg) {
cfg.setAttributeSplittingDisabled(true);
cfg.setDelimiterParsingDisabled(true);
@@ -225,7 +264,7 @@
}
// Expands the specified ZIP stream into app-specific directory.
- private void expandApplication(InputStream stream, ApplicationDescription desc)
+ private void expandZippedApplication(InputStream stream, ApplicationDescription desc)
throws IOException {
ZipInputStream zis = new ZipInputStream(stream);
ZipEntry entry;
@@ -234,7 +273,6 @@
if (!entry.isDirectory()) {
byte[] data = ByteStreams.toByteArray(zis);
zis.closeEntry();
-
File file = new File(appDir, entry.getName());
createParentDirs(file);
write(data, file);
@@ -243,6 +281,15 @@
zis.close();
}
+ // Saves the specified XML stream into app-specific directory.
+ private void expandPlainApplication(byte[] stream, ApplicationDescription desc)
+ throws IOException {
+ File file = appFile(desc.name(), APP_XML);
+ createParentDirs(file);
+ write(stream, file);
+ }
+
+
// Saves the specified ZIP stream into a file under app-specific directory.
private void saveApplication(InputStream stream, ApplicationDescription desc)
throws IOException {