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/apps/config/app.xml b/apps/config/app.xml
new file mode 100644
index 0000000..b561670
--- /dev/null
+++ b/apps/config/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.config" origin="ON.Lab" version="1.1.0"
+ features="onos-app-config">
+ <description>ONOS network configuration application</description>
+</app>
diff --git a/apps/fwd/app.xml b/apps/fwd/app.xml
new file mode 100644
index 0000000..fad1927
--- /dev/null
+++ b/apps/fwd/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.fwd" origin="ON.Lab" version="1.1.0"
+ features="onos-app-fwd">
+ <description>ONOS Reactive forwarding application using flow subsystem</description>
+</app>
diff --git a/apps/ifwd/app.xml b/apps/ifwd/app.xml
new file mode 100644
index 0000000..9403d06
--- /dev/null
+++ b/apps/ifwd/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.ifwd" origin="ON.Lab" version="1.1.0"
+ features="onos-app-ifwd">
+ <description>ONOS Reactive forwarding application using intent subsystem (experimental)</description>
+</app>
diff --git a/apps/metrics/intent/app.xml b/apps/metrics/intent/app.xml
new file mode 100644
index 0000000..4d184f2
--- /dev/null
+++ b/apps/metrics/intent/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.metrics.intent" origin="ON.Lab" version="1.1.0"
+ features="onos-app-metrics-intent">
+ <description>ONOS intent metrics test application</description>
+</app>
diff --git a/apps/metrics/topology/app.xml b/apps/metrics/topology/app.xml
new file mode 100644
index 0000000..2dd9d53
--- /dev/null
+++ b/apps/metrics/topology/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.metrics.topology" origin="ON.Lab" version="1.1.0"
+ features="onos-app-metrics-topology">
+ <description>ONOS topology metrics test application</description>
+</app>
diff --git a/apps/optical/app.xml b/apps/optical/app.xml
new file mode 100644
index 0000000..92877d9
--- /dev/null
+++ b/apps/optical/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.optical" origin="ON.Lab" version="1.1.0"
+ features="onos-app-sdnip">
+ <description>ONOS Packet/Optical use-case application</description>
+</app>
diff --git a/apps/proxyarp/app.xml b/apps/proxyarp/app.xml
new file mode 100644
index 0000000..6dad95d
--- /dev/null
+++ b/apps/proxyarp/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.proxyarp" origin="ON.Lab" version="1.1.0"
+ features="onos-app-proxyarp">
+ <description>ONOS proxy ARP application</description>
+</app>
diff --git a/apps/sdnip/app.xml b/apps/sdnip/app.xml
new file mode 100644
index 0000000..3264d90
--- /dev/null
+++ b/apps/sdnip/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.sdnip" origin="ON.Lab" version="1.1.0"
+ features="onos-app-sdnip">
+ <description>ONOS SDN/IP use-case application</description>
+</app>
diff --git a/apps/tvue/app.xml b/apps/tvue/app.xml
new file mode 100644
index 0000000..1435903
--- /dev/null
+++ b/apps/tvue/app.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2015 Open Networking Laboratory
+ ~
+ ~ Licensed 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.
+ -->
+<app name="org.onosproject.app.tvue" origin="ON.Lab" version="1.1.0"
+ features="onos-app-tvue">
+ <description>Early prototype GUI (deprecated)</description>
+</app>
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationAdminService.java b/core/api/src/main/java/org/onosproject/app/ApplicationAdminService.java
index 18babd5..e0ea6ec 100644
--- a/core/api/src/main/java/org/onosproject/app/ApplicationAdminService.java
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationAdminService.java
@@ -29,7 +29,9 @@
/**
* Installs the application contained in the specified application archive
- * input stream.
+ * input stream. This can be either a ZIP stream containing a compressed
+ * application archive or a plain XML stream containing just the
+ * {@code app.xml} application descriptor file.
*
* @param appDescStream application descriptor input stream
* @return installed application descriptor
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 {
diff --git a/core/common/src/test/java/org/onosproject/common/app/ApplicationArchiveTest.java b/core/common/src/test/java/org/onosproject/common/app/ApplicationArchiveTest.java
index 3d86dda..240ed96 100644
--- a/core/common/src/test/java/org/onosproject/common/app/ApplicationArchiveTest.java
+++ b/core/common/src/test/java/org/onosproject/common/app/ApplicationArchiveTest.java
@@ -30,8 +30,7 @@
import java.util.Random;
import java.util.Set;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import static org.onosproject.app.DefaultApplicationDescriptionTest.*;
public class ApplicationArchiveTest {
@@ -64,43 +63,69 @@
}
@Test
- public void saveApp() throws IOException {
+ public void saveZippedApp() throws IOException {
InputStream stream = getClass().getResourceAsStream("app.zip");
ApplicationDescription app = aar.saveApplication(stream);
validate(app);
}
@Test
+ public void savePlainApp() throws IOException {
+ InputStream stream = getClass().getResourceAsStream("app.xml");
+ ApplicationDescription app = aar.saveApplication(stream);
+ validate(app);
+ }
+
+ @Test
public void loadApp() throws IOException {
- saveApp();
+ saveZippedApp();
ApplicationDescription app = aar.getApplicationDescription(APP_NAME);
validate(app);
}
@Test
public void getAppNames() throws IOException {
- saveApp();
+ saveZippedApp();
Set<String> names = aar.getApplicationNames();
assertEquals("incorrect names", ImmutableSet.of(APP_NAME), names);
}
@Test
public void purgeApp() throws IOException {
- saveApp();
+ saveZippedApp();
aar.purgeApplication(APP_NAME);
assertEquals("incorrect names", ImmutableSet.<String>of(),
aar.getApplicationNames());
}
@Test
- public void getAppStream() throws IOException {
- saveApp();
+ public void getAppZipStream() throws IOException {
+ saveZippedApp();
InputStream stream = aar.getApplicationInputStream(APP_NAME);
byte[] orig = ByteStreams.toByteArray(getClass().getResourceAsStream("app.zip"));
byte[] loaded = ByteStreams.toByteArray(stream);
assertArrayEquals("incorrect stream", orig, loaded);
}
+ @Test
+ public void getAppXmlStream() throws IOException {
+ savePlainApp();
+ InputStream stream = aar.getApplicationInputStream(APP_NAME);
+ byte[] orig = ByteStreams.toByteArray(getClass().getResourceAsStream("app.xml"));
+ byte[] loaded = ByteStreams.toByteArray(stream);
+ assertArrayEquals("incorrect stream", orig, loaded);
+ }
+
+ @Test
+ public void active() throws IOException {
+ savePlainApp();
+ assertFalse("should not be active", aar.isActive(APP_NAME));
+ aar.setActive(APP_NAME);
+ assertTrue("should not be active", aar.isActive(APP_NAME));
+ aar.clearActive(APP_NAME);
+ assertFalse("should not be active", aar.isActive(APP_NAME));
+ }
+
@Test(expected = ApplicationException.class)
public void getBadAppDesc() throws IOException {
aar.getApplicationDescription("org.foo.BAD");
@@ -111,4 +136,14 @@
aar.getApplicationInputStream("org.foo.BAD");
}
+ @Test(expected = ApplicationException.class)
+ public void setBadActive() throws IOException {
+ aar.setActive("org.foo.BAD");
+ }
+
+ @Test(expected = ApplicationException.class)
+ public void purgeBadApp() throws IOException {
+ aar.purgeApplication("org.foo.BAD");
+ }
+
}
\ No newline at end of file
diff --git a/core/common/src/test/resources/org/onosproject/common/app/app.xml b/core/common/src/test/resources/org/onosproject/common/app/app.xml
index 3b8bc62..39f328b 100644
--- a/core/common/src/test/resources/org/onosproject/common/app/app.xml
+++ b/core/common/src/test/resources/org/onosproject/common/app/app.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
diff --git a/features/features.xml b/features/features.xml
index 9440f52..aed7d7c 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -16,7 +16,8 @@
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
name="onos-@FEATURE-VERSION">
- <repository>mvn:org.onosproject/onos-features/@ONOS-VERSION/xml/features</repository>
+ <repository>mvn:org.onosproject/onos-features/@ONOS-VERSION/xml/features
+ </repository>
<feature name="onos-thirdparty-base" version="@FEATURE-VERSION"
description="ONOS 3rd party dependencies">
@@ -126,7 +127,7 @@
</feature>
<feature name="onos-null" version="@FEATURE-VERSION"
- description="ONOS Null providers">
+ description="ONOS Null providers">
<feature>onos-api</feature>
<bundle>mvn:org.onosproject/onos-null-provider-device/@ONOS-VERSION</bundle>
@@ -197,12 +198,12 @@
<feature>onos-api</feature>
<bundle>mvn:org.onosproject/onos-app-config/@ONOS-VERSION</bundle>
</feature>
-
- <feature name="onos-app-optical" version="@FEATURE-VERSION"
+
+ <feature name="onos-app-optical" version="@FEATURE-VERSION"
description="ONOS optical network config">
<feature>onos-api</feature>
<bundle>mvn:org.onosproject/onos-app-optical/@ONOS-VERSION</bundle>
- </feature>
+ </feature>
<feature name="onos-app-sdnip" version="@FEATURE-VERSION"
description="SDN-IP peering application">
@@ -210,8 +211,8 @@
<feature>onos-app-proxyarp</feature>
<feature>onos-app-config</feature>
<bundle>mvn:org.onosproject/onos-app-sdnip/@ONOS-VERSION</bundle>
- <bundle>mvn:org.onosproject/onos-app-routing-api/@ONOS-VERSION</bundle>
- <bundle>mvn:org.onosproject/onos-app-routing/@ONOS-VERSION</bundle>
+ <bundle>mvn:org.onosproject/onos-app-routing-api/@ONOS-VERSION</bundle>
+ <bundle>mvn:org.onosproject/onos-app-routing/@ONOS-VERSION</bundle>
</feature>
<feature name="onos-app-calendar" version="@FEATURE-VERSION"
diff --git a/tools/test/bin/onos-app b/tools/test/bin/onos-app
index d474ef3..31b87d3 100755
--- a/tools/test/bin/onos-app
+++ b/tools/test/bin/onos-app
@@ -14,7 +14,11 @@
case $cmd in
list) $curl -X GET $URL;;
install) $curl -X POST $HDR $URL --data-binary @$app;;
+ install!) $curl -X POST $HDR $URL?activate=true --data-binary @$app;;
uninstall) $curl -X DELETE $URL/$app;;
activate) $curl -X POST $URL/$app/active;;
deactivate) $curl -X DELETE $URL/$app/active;;
+ *) echo "usage: onos-app {install|install!} <app-file>" >&2
+ echo " onos-app {activate|deactivate|uninstall} <app-name>" >&2
+ exit 1;;
esac
diff --git a/utils/misc/src/main/java/org/onlab/util/Tools.java b/utils/misc/src/main/java/org/onlab/util/Tools.java
index 9e690c0..e366172 100644
--- a/utils/misc/src/main/java/org/onlab/util/Tools.java
+++ b/utils/misc/src/main/java/org/onlab/util/Tools.java
@@ -182,7 +182,11 @@
* @throws java.io.IOException if unable to remove contents
*/
public static void removeDirectory(String path) throws IOException {
- walkFileTree(Paths.get(path), new DirectoryDeleter());
+ DirectoryDeleter visitor = new DirectoryDeleter();
+ walkFileTree(Paths.get(path), visitor);
+ if (visitor.exception != null) {
+ throw visitor.exception;
+ }
}
/**
@@ -194,11 +198,18 @@
* @throws java.io.IOException if unable to remove contents
*/
public static void removeDirectory(File dir) throws IOException {
- walkFileTree(Paths.get(dir.getAbsolutePath()), new DirectoryDeleter());
+ DirectoryDeleter visitor = new DirectoryDeleter();
+ walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
+ if (visitor.exception != null) {
+ throw visitor.exception;
+ }
}
-
+ // Auxiliary path visitor for recursive directory structure removal.
private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
+
+ private IOException exception;
+
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
throws IOException {
@@ -218,9 +229,8 @@
@Override
public FileVisitResult visitFileFailed(Path file, IOException ioe)
throws IOException {
- log.warn("Unable to delete file {}", file);
- log.warn("Boom", ioe);
- return FileVisitResult.CONTINUE;
+ this.exception = ioe;
+ return FileVisitResult.TERMINATE;
}
}
@@ -253,8 +263,8 @@
dst.getAbsolutePath()));
}
-
- public static class DirectoryCopier extends SimpleFileVisitor<Path> {
+ // Auxiliary path visitor for recursive directory structure copying.
+ private static class DirectoryCopier extends SimpleFileVisitor<Path> {
private Path src;
private Path dst;
private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
diff --git a/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java b/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java
index 9d32090..1b3bd3a 100644
--- a/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/ApplicationsWebResource.java
@@ -21,11 +21,13 @@
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
@@ -55,9 +57,14 @@
@POST
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
@Produces(MediaType.APPLICATION_JSON)
- public Response installApplication(InputStream stream) {
+ public Response installApplication(@QueryParam("activate")
+ @DefaultValue("false") boolean activate,
+ InputStream stream) {
ApplicationAdminService service = get(ApplicationAdminService.class);
Application app = service.install(stream);
+ if (activate) {
+ service.activate(app.id());
+ }
return ok(codec(Application.class).encode(app, this)).build();
}