Committed by
Gerrit Code Review
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
Showing
17 changed files
with
321 additions
and
34 deletions
apps/config/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.config" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-config"> | ||
19 | + <description>ONOS network configuration application</description> | ||
20 | +</app> |
apps/fwd/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.fwd" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-fwd"> | ||
19 | + <description>ONOS Reactive forwarding application using flow subsystem</description> | ||
20 | +</app> |
apps/ifwd/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.ifwd" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-ifwd"> | ||
19 | + <description>ONOS Reactive forwarding application using intent subsystem (experimental)</description> | ||
20 | +</app> |
apps/metrics/intent/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.metrics.intent" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-metrics-intent"> | ||
19 | + <description>ONOS intent metrics test application</description> | ||
20 | +</app> |
apps/metrics/topology/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.metrics.topology" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-metrics-topology"> | ||
19 | + <description>ONOS topology metrics test application</description> | ||
20 | +</app> |
apps/optical/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.optical" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-sdnip"> | ||
19 | + <description>ONOS Packet/Optical use-case application</description> | ||
20 | +</app> |
apps/proxyarp/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.proxyarp" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-proxyarp"> | ||
19 | + <description>ONOS proxy ARP application</description> | ||
20 | +</app> |
apps/sdnip/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.sdnip" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-sdnip"> | ||
19 | + <description>ONOS SDN/IP use-case application</description> | ||
20 | +</app> |
apps/tvue/app.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2015 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<app name="org.onosproject.app.tvue" origin="ON.Lab" version="1.1.0" | ||
18 | + features="onos-app-tvue"> | ||
19 | + <description>Early prototype GUI (deprecated)</description> | ||
20 | +</app> |
... | @@ -29,7 +29,9 @@ public interface ApplicationAdminService extends ApplicationService { | ... | @@ -29,7 +29,9 @@ public interface ApplicationAdminService extends ApplicationService { |
29 | 29 | ||
30 | /** | 30 | /** |
31 | * Installs the application contained in the specified application archive | 31 | * Installs the application contained in the specified application archive |
32 | - * input stream. | 32 | + * input stream. This can be either a ZIP stream containing a compressed |
33 | + * application archive or a plain XML stream containing just the | ||
34 | + * {@code app.xml} application descriptor file. | ||
33 | * | 35 | * |
34 | * @param appDescStream application descriptor input stream | 36 | * @param appDescStream application descriptor input stream |
35 | * @return installed application descriptor | 37 | * @return installed application descriptor | ... | ... |
... | @@ -40,6 +40,7 @@ import java.io.FileNotFoundException; | ... | @@ -40,6 +40,7 @@ import java.io.FileNotFoundException; |
40 | import java.io.IOException; | 40 | import java.io.IOException; |
41 | import java.io.InputStream; | 41 | import java.io.InputStream; |
42 | import java.net.URI; | 42 | import java.net.URI; |
43 | +import java.nio.charset.Charset; | ||
43 | import java.nio.file.NoSuchFileException; | 44 | import java.nio.file.NoSuchFileException; |
44 | import java.util.List; | 45 | import java.util.List; |
45 | import java.util.Set; | 46 | import java.util.Set; |
... | @@ -57,6 +58,13 @@ import static com.google.common.io.Files.write; | ... | @@ -57,6 +58,13 @@ import static com.google.common.io.Files.write; |
57 | public class ApplicationArchive | 58 | public class ApplicationArchive |
58 | extends AbstractStore<ApplicationEvent, ApplicationStoreDelegate> { | 59 | extends AbstractStore<ApplicationEvent, ApplicationStoreDelegate> { |
59 | 60 | ||
61 | + // Magic strings to search for at the beginning of the archive stream | ||
62 | + private static final String XML_MAGIC = "<?xml "; | ||
63 | + | ||
64 | + // Magic strings to search for and how deep to search it into the archive stream | ||
65 | + private static final String APP_MAGIC = "<app "; | ||
66 | + private static final int APP_MAGIC_DEPTH = 1024; | ||
67 | + | ||
60 | private static final String NAME = "[@name]"; | 68 | private static final String NAME = "[@name]"; |
61 | private static final String ORIGIN = "[@origin]"; | 69 | private static final String ORIGIN = "[@origin]"; |
62 | private static final String VERSION = "[@version]"; | 70 | private static final String VERSION = "[@version]"; |
... | @@ -144,13 +152,21 @@ public class ApplicationArchive | ... | @@ -144,13 +152,21 @@ public class ApplicationArchive |
144 | try (InputStream ais = stream) { | 152 | try (InputStream ais = stream) { |
145 | byte[] cache = toByteArray(ais); | 153 | byte[] cache = toByteArray(ais); |
146 | InputStream bis = new ByteArrayInputStream(cache); | 154 | InputStream bis = new ByteArrayInputStream(cache); |
147 | - ApplicationDescription desc = parseAppDescription(bis); | ||
148 | - bis.reset(); | ||
149 | 155 | ||
150 | - expandApplication(bis, desc); | 156 | + boolean plainXml = isPlainXml(cache); |
157 | + ApplicationDescription desc = plainXml ? | ||
158 | + parsePlainAppDescription(bis) : parseZippedAppDescription(bis); | ||
159 | + | ||
160 | + if (plainXml) { | ||
161 | + expandPlainApplication(cache, desc); | ||
162 | + } else { | ||
151 | bis.reset(); | 163 | bis.reset(); |
164 | + expandZippedApplication(bis, desc); | ||
152 | 165 | ||
166 | + bis.reset(); | ||
153 | saveApplication(bis, desc); | 167 | saveApplication(bis, desc); |
168 | + } | ||
169 | + | ||
154 | installArtifacts(desc); | 170 | installArtifacts(desc); |
155 | return desc; | 171 | return desc; |
156 | } catch (IOException e) { | 172 | } catch (IOException e) { |
... | @@ -158,28 +174,45 @@ public class ApplicationArchive | ... | @@ -158,28 +174,45 @@ public class ApplicationArchive |
158 | } | 174 | } |
159 | } | 175 | } |
160 | 176 | ||
177 | + // Indicates whether the stream encoded in the given bytes is plain XML. | ||
178 | + private boolean isPlainXml(byte[] bytes) { | ||
179 | + return substring(bytes, XML_MAGIC.length()).equals(XML_MAGIC) || | ||
180 | + substring(bytes, APP_MAGIC_DEPTH).contains(APP_MAGIC); | ||
181 | + } | ||
182 | + | ||
183 | + // Returns the substring of maximum possible length from the specified bytes. | ||
184 | + private String substring(byte[] bytes, int length) { | ||
185 | + return new String(bytes, 0, Math.min(bytes.length, length), Charset.forName("UTF-8")); | ||
186 | + } | ||
187 | + | ||
161 | /** | 188 | /** |
162 | * Purges the application archive directory. | 189 | * Purges the application archive directory. |
163 | * | 190 | * |
164 | * @param appName application name | 191 | * @param appName application name |
165 | */ | 192 | */ |
166 | public void purgeApplication(String appName) { | 193 | public void purgeApplication(String appName) { |
194 | + File appDir = new File(appsDir, appName); | ||
167 | try { | 195 | try { |
168 | - Tools.removeDirectory(new File(appsDir, appName)); | 196 | + Tools.removeDirectory(appDir); |
169 | } catch (IOException e) { | 197 | } catch (IOException e) { |
170 | throw new ApplicationException("Unable to purge application " + appName, e); | 198 | throw new ApplicationException("Unable to purge application " + appName, e); |
171 | } | 199 | } |
200 | + if (appDir.exists()) { | ||
201 | + throw new ApplicationException("Unable to purge application " + appName); | ||
202 | + } | ||
172 | } | 203 | } |
173 | 204 | ||
174 | /** | 205 | /** |
175 | - * Returns application archive stream for the specified application. | 206 | + * Returns application archive stream for the specified application. This |
207 | + * will be either the application ZIP file or the application XML file. | ||
176 | * | 208 | * |
177 | * @param appName application name | 209 | * @param appName application name |
178 | * @return application archive stream | 210 | * @return application archive stream |
179 | */ | 211 | */ |
180 | public InputStream getApplicationInputStream(String appName) { | 212 | public InputStream getApplicationInputStream(String appName) { |
181 | try { | 213 | try { |
182 | - return new FileInputStream(appFile(appName, appName + ".zip")); | 214 | + File appFile = appFile(appName, appName + ".zip"); |
215 | + return new FileInputStream(appFile.exists() ? appFile : appFile(appName, APP_XML)); | ||
183 | } catch (FileNotFoundException e) { | 216 | } catch (FileNotFoundException e) { |
184 | throw new ApplicationException("Application " + appName + " not found"); | 217 | throw new ApplicationException("Application " + appName + " not found"); |
185 | } | 218 | } |
... | @@ -187,26 +220,32 @@ public class ApplicationArchive | ... | @@ -187,26 +220,32 @@ public class ApplicationArchive |
187 | 220 | ||
188 | // Scans the specified ZIP stream for app.xml entry and parses it producing | 221 | // Scans the specified ZIP stream for app.xml entry and parses it producing |
189 | // an application descriptor. | 222 | // an application descriptor. |
190 | - private ApplicationDescription parseAppDescription(InputStream stream) | 223 | + private ApplicationDescription parseZippedAppDescription(InputStream stream) |
191 | throws IOException { | 224 | throws IOException { |
192 | try (ZipInputStream zis = new ZipInputStream(stream)) { | 225 | try (ZipInputStream zis = new ZipInputStream(stream)) { |
193 | ZipEntry entry; | 226 | ZipEntry entry; |
194 | while ((entry = zis.getNextEntry()) != null) { | 227 | while ((entry = zis.getNextEntry()) != null) { |
195 | if (entry.getName().equals(APP_XML)) { | 228 | if (entry.getName().equals(APP_XML)) { |
196 | byte[] data = ByteStreams.toByteArray(zis); | 229 | byte[] data = ByteStreams.toByteArray(zis); |
230 | + return parsePlainAppDescription(new ByteArrayInputStream(data)); | ||
231 | + } | ||
232 | + zis.closeEntry(); | ||
233 | + } | ||
234 | + } | ||
235 | + throw new IOException("Unable to locate " + APP_XML); | ||
236 | + } | ||
237 | + | ||
238 | + // Scans the specified XML stream and parses it producing an application descriptor. | ||
239 | + private ApplicationDescription parsePlainAppDescription(InputStream stream) | ||
240 | + throws IOException { | ||
197 | XMLConfiguration cfg = new XMLConfiguration(); | 241 | XMLConfiguration cfg = new XMLConfiguration(); |
198 | try { | 242 | try { |
199 | - cfg.load(new ByteArrayInputStream(data)); | 243 | + cfg.load(stream); |
200 | return loadAppDescription(cfg); | 244 | return loadAppDescription(cfg); |
201 | } catch (ConfigurationException e) { | 245 | } catch (ConfigurationException e) { |
202 | throw new IOException("Unable to parse " + APP_XML, e); | 246 | throw new IOException("Unable to parse " + APP_XML, e); |
203 | } | 247 | } |
204 | } | 248 | } |
205 | - zis.closeEntry(); | ||
206 | - } | ||
207 | - } | ||
208 | - throw new IOException("Unable to locate " + APP_XML); | ||
209 | - } | ||
210 | 249 | ||
211 | private ApplicationDescription loadAppDescription(XMLConfiguration cfg) { | 250 | private ApplicationDescription loadAppDescription(XMLConfiguration cfg) { |
212 | cfg.setAttributeSplittingDisabled(true); | 251 | cfg.setAttributeSplittingDisabled(true); |
... | @@ -225,7 +264,7 @@ public class ApplicationArchive | ... | @@ -225,7 +264,7 @@ public class ApplicationArchive |
225 | } | 264 | } |
226 | 265 | ||
227 | // Expands the specified ZIP stream into app-specific directory. | 266 | // Expands the specified ZIP stream into app-specific directory. |
228 | - private void expandApplication(InputStream stream, ApplicationDescription desc) | 267 | + private void expandZippedApplication(InputStream stream, ApplicationDescription desc) |
229 | throws IOException { | 268 | throws IOException { |
230 | ZipInputStream zis = new ZipInputStream(stream); | 269 | ZipInputStream zis = new ZipInputStream(stream); |
231 | ZipEntry entry; | 270 | ZipEntry entry; |
... | @@ -234,7 +273,6 @@ public class ApplicationArchive | ... | @@ -234,7 +273,6 @@ public class ApplicationArchive |
234 | if (!entry.isDirectory()) { | 273 | if (!entry.isDirectory()) { |
235 | byte[] data = ByteStreams.toByteArray(zis); | 274 | byte[] data = ByteStreams.toByteArray(zis); |
236 | zis.closeEntry(); | 275 | zis.closeEntry(); |
237 | - | ||
238 | File file = new File(appDir, entry.getName()); | 276 | File file = new File(appDir, entry.getName()); |
239 | createParentDirs(file); | 277 | createParentDirs(file); |
240 | write(data, file); | 278 | write(data, file); |
... | @@ -243,6 +281,15 @@ public class ApplicationArchive | ... | @@ -243,6 +281,15 @@ public class ApplicationArchive |
243 | zis.close(); | 281 | zis.close(); |
244 | } | 282 | } |
245 | 283 | ||
284 | + // Saves the specified XML stream into app-specific directory. | ||
285 | + private void expandPlainApplication(byte[] stream, ApplicationDescription desc) | ||
286 | + throws IOException { | ||
287 | + File file = appFile(desc.name(), APP_XML); | ||
288 | + createParentDirs(file); | ||
289 | + write(stream, file); | ||
290 | + } | ||
291 | + | ||
292 | + | ||
246 | // Saves the specified ZIP stream into a file under app-specific directory. | 293 | // Saves the specified ZIP stream into a file under app-specific directory. |
247 | private void saveApplication(InputStream stream, ApplicationDescription desc) | 294 | private void saveApplication(InputStream stream, ApplicationDescription desc) |
248 | throws IOException { | 295 | throws IOException { | ... | ... |
... | @@ -30,8 +30,7 @@ import java.io.InputStream; | ... | @@ -30,8 +30,7 @@ import java.io.InputStream; |
30 | import java.util.Random; | 30 | import java.util.Random; |
31 | import java.util.Set; | 31 | import java.util.Set; |
32 | 32 | ||
33 | -import static org.junit.Assert.assertArrayEquals; | 33 | +import static org.junit.Assert.*; |
34 | -import static org.junit.Assert.assertEquals; | ||
35 | import static org.onosproject.app.DefaultApplicationDescriptionTest.*; | 34 | import static org.onosproject.app.DefaultApplicationDescriptionTest.*; |
36 | 35 | ||
37 | public class ApplicationArchiveTest { | 36 | public class ApplicationArchiveTest { |
... | @@ -64,43 +63,69 @@ public class ApplicationArchiveTest { | ... | @@ -64,43 +63,69 @@ public class ApplicationArchiveTest { |
64 | } | 63 | } |
65 | 64 | ||
66 | @Test | 65 | @Test |
67 | - public void saveApp() throws IOException { | 66 | + public void saveZippedApp() throws IOException { |
68 | InputStream stream = getClass().getResourceAsStream("app.zip"); | 67 | InputStream stream = getClass().getResourceAsStream("app.zip"); |
69 | ApplicationDescription app = aar.saveApplication(stream); | 68 | ApplicationDescription app = aar.saveApplication(stream); |
70 | validate(app); | 69 | validate(app); |
71 | } | 70 | } |
72 | 71 | ||
73 | @Test | 72 | @Test |
73 | + public void savePlainApp() throws IOException { | ||
74 | + InputStream stream = getClass().getResourceAsStream("app.xml"); | ||
75 | + ApplicationDescription app = aar.saveApplication(stream); | ||
76 | + validate(app); | ||
77 | + } | ||
78 | + | ||
79 | + @Test | ||
74 | public void loadApp() throws IOException { | 80 | public void loadApp() throws IOException { |
75 | - saveApp(); | 81 | + saveZippedApp(); |
76 | ApplicationDescription app = aar.getApplicationDescription(APP_NAME); | 82 | ApplicationDescription app = aar.getApplicationDescription(APP_NAME); |
77 | validate(app); | 83 | validate(app); |
78 | } | 84 | } |
79 | 85 | ||
80 | @Test | 86 | @Test |
81 | public void getAppNames() throws IOException { | 87 | public void getAppNames() throws IOException { |
82 | - saveApp(); | 88 | + saveZippedApp(); |
83 | Set<String> names = aar.getApplicationNames(); | 89 | Set<String> names = aar.getApplicationNames(); |
84 | assertEquals("incorrect names", ImmutableSet.of(APP_NAME), names); | 90 | assertEquals("incorrect names", ImmutableSet.of(APP_NAME), names); |
85 | } | 91 | } |
86 | 92 | ||
87 | @Test | 93 | @Test |
88 | public void purgeApp() throws IOException { | 94 | public void purgeApp() throws IOException { |
89 | - saveApp(); | 95 | + saveZippedApp(); |
90 | aar.purgeApplication(APP_NAME); | 96 | aar.purgeApplication(APP_NAME); |
91 | assertEquals("incorrect names", ImmutableSet.<String>of(), | 97 | assertEquals("incorrect names", ImmutableSet.<String>of(), |
92 | aar.getApplicationNames()); | 98 | aar.getApplicationNames()); |
93 | } | 99 | } |
94 | 100 | ||
95 | @Test | 101 | @Test |
96 | - public void getAppStream() throws IOException { | 102 | + public void getAppZipStream() throws IOException { |
97 | - saveApp(); | 103 | + saveZippedApp(); |
98 | InputStream stream = aar.getApplicationInputStream(APP_NAME); | 104 | InputStream stream = aar.getApplicationInputStream(APP_NAME); |
99 | byte[] orig = ByteStreams.toByteArray(getClass().getResourceAsStream("app.zip")); | 105 | byte[] orig = ByteStreams.toByteArray(getClass().getResourceAsStream("app.zip")); |
100 | byte[] loaded = ByteStreams.toByteArray(stream); | 106 | byte[] loaded = ByteStreams.toByteArray(stream); |
101 | assertArrayEquals("incorrect stream", orig, loaded); | 107 | assertArrayEquals("incorrect stream", orig, loaded); |
102 | } | 108 | } |
103 | 109 | ||
110 | + @Test | ||
111 | + public void getAppXmlStream() throws IOException { | ||
112 | + savePlainApp(); | ||
113 | + InputStream stream = aar.getApplicationInputStream(APP_NAME); | ||
114 | + byte[] orig = ByteStreams.toByteArray(getClass().getResourceAsStream("app.xml")); | ||
115 | + byte[] loaded = ByteStreams.toByteArray(stream); | ||
116 | + assertArrayEquals("incorrect stream", orig, loaded); | ||
117 | + } | ||
118 | + | ||
119 | + @Test | ||
120 | + public void active() throws IOException { | ||
121 | + savePlainApp(); | ||
122 | + assertFalse("should not be active", aar.isActive(APP_NAME)); | ||
123 | + aar.setActive(APP_NAME); | ||
124 | + assertTrue("should not be active", aar.isActive(APP_NAME)); | ||
125 | + aar.clearActive(APP_NAME); | ||
126 | + assertFalse("should not be active", aar.isActive(APP_NAME)); | ||
127 | + } | ||
128 | + | ||
104 | @Test(expected = ApplicationException.class) | 129 | @Test(expected = ApplicationException.class) |
105 | public void getBadAppDesc() throws IOException { | 130 | public void getBadAppDesc() throws IOException { |
106 | aar.getApplicationDescription("org.foo.BAD"); | 131 | aar.getApplicationDescription("org.foo.BAD"); |
... | @@ -111,4 +136,14 @@ public class ApplicationArchiveTest { | ... | @@ -111,4 +136,14 @@ public class ApplicationArchiveTest { |
111 | aar.getApplicationInputStream("org.foo.BAD"); | 136 | aar.getApplicationInputStream("org.foo.BAD"); |
112 | } | 137 | } |
113 | 138 | ||
139 | + @Test(expected = ApplicationException.class) | ||
140 | + public void setBadActive() throws IOException { | ||
141 | + aar.setActive("org.foo.BAD"); | ||
142 | + } | ||
143 | + | ||
144 | + @Test(expected = ApplicationException.class) | ||
145 | + public void purgeBadApp() throws IOException { | ||
146 | + aar.purgeApplication("org.foo.BAD"); | ||
147 | + } | ||
148 | + | ||
114 | } | 149 | } |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -16,7 +16,8 @@ | ... | @@ -16,7 +16,8 @@ |
16 | --> | 16 | --> |
17 | <features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" | 17 | <features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" |
18 | name="onos-@FEATURE-VERSION"> | 18 | name="onos-@FEATURE-VERSION"> |
19 | - <repository>mvn:org.onosproject/onos-features/@ONOS-VERSION/xml/features</repository> | 19 | + <repository>mvn:org.onosproject/onos-features/@ONOS-VERSION/xml/features |
20 | + </repository> | ||
20 | 21 | ||
21 | <feature name="onos-thirdparty-base" version="@FEATURE-VERSION" | 22 | <feature name="onos-thirdparty-base" version="@FEATURE-VERSION" |
22 | description="ONOS 3rd party dependencies"> | 23 | description="ONOS 3rd party dependencies"> | ... | ... |
... | @@ -14,7 +14,11 @@ export curl="curl -sS" | ... | @@ -14,7 +14,11 @@ export curl="curl -sS" |
14 | case $cmd in | 14 | case $cmd in |
15 | list) $curl -X GET $URL;; | 15 | list) $curl -X GET $URL;; |
16 | install) $curl -X POST $HDR $URL --data-binary @$app;; | 16 | install) $curl -X POST $HDR $URL --data-binary @$app;; |
17 | + install!) $curl -X POST $HDR $URL?activate=true --data-binary @$app;; | ||
17 | uninstall) $curl -X DELETE $URL/$app;; | 18 | uninstall) $curl -X DELETE $URL/$app;; |
18 | activate) $curl -X POST $URL/$app/active;; | 19 | activate) $curl -X POST $URL/$app/active;; |
19 | deactivate) $curl -X DELETE $URL/$app/active;; | 20 | deactivate) $curl -X DELETE $URL/$app/active;; |
21 | + *) echo "usage: onos-app {install|install!} <app-file>" >&2 | ||
22 | + echo " onos-app {activate|deactivate|uninstall} <app-name>" >&2 | ||
23 | + exit 1;; | ||
20 | esac | 24 | esac | ... | ... |
... | @@ -182,7 +182,11 @@ public abstract class Tools { | ... | @@ -182,7 +182,11 @@ public abstract class Tools { |
182 | * @throws java.io.IOException if unable to remove contents | 182 | * @throws java.io.IOException if unable to remove contents |
183 | */ | 183 | */ |
184 | public static void removeDirectory(String path) throws IOException { | 184 | public static void removeDirectory(String path) throws IOException { |
185 | - walkFileTree(Paths.get(path), new DirectoryDeleter()); | 185 | + DirectoryDeleter visitor = new DirectoryDeleter(); |
186 | + walkFileTree(Paths.get(path), visitor); | ||
187 | + if (visitor.exception != null) { | ||
188 | + throw visitor.exception; | ||
189 | + } | ||
186 | } | 190 | } |
187 | 191 | ||
188 | /** | 192 | /** |
... | @@ -194,11 +198,18 @@ public abstract class Tools { | ... | @@ -194,11 +198,18 @@ public abstract class Tools { |
194 | * @throws java.io.IOException if unable to remove contents | 198 | * @throws java.io.IOException if unable to remove contents |
195 | */ | 199 | */ |
196 | public static void removeDirectory(File dir) throws IOException { | 200 | public static void removeDirectory(File dir) throws IOException { |
197 | - walkFileTree(Paths.get(dir.getAbsolutePath()), new DirectoryDeleter()); | 201 | + DirectoryDeleter visitor = new DirectoryDeleter(); |
202 | + walkFileTree(Paths.get(dir.getAbsolutePath()), visitor); | ||
203 | + if (visitor.exception != null) { | ||
204 | + throw visitor.exception; | ||
205 | + } | ||
198 | } | 206 | } |
199 | 207 | ||
200 | - | 208 | + // Auxiliary path visitor for recursive directory structure removal. |
201 | private static class DirectoryDeleter extends SimpleFileVisitor<Path> { | 209 | private static class DirectoryDeleter extends SimpleFileVisitor<Path> { |
210 | + | ||
211 | + private IOException exception; | ||
212 | + | ||
202 | @Override | 213 | @Override |
203 | public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) | 214 | public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) |
204 | throws IOException { | 215 | throws IOException { |
... | @@ -218,9 +229,8 @@ public abstract class Tools { | ... | @@ -218,9 +229,8 @@ public abstract class Tools { |
218 | @Override | 229 | @Override |
219 | public FileVisitResult visitFileFailed(Path file, IOException ioe) | 230 | public FileVisitResult visitFileFailed(Path file, IOException ioe) |
220 | throws IOException { | 231 | throws IOException { |
221 | - log.warn("Unable to delete file {}", file); | 232 | + this.exception = ioe; |
222 | - log.warn("Boom", ioe); | 233 | + return FileVisitResult.TERMINATE; |
223 | - return FileVisitResult.CONTINUE; | ||
224 | } | 234 | } |
225 | } | 235 | } |
226 | 236 | ||
... | @@ -253,8 +263,8 @@ public abstract class Tools { | ... | @@ -253,8 +263,8 @@ public abstract class Tools { |
253 | dst.getAbsolutePath())); | 263 | dst.getAbsolutePath())); |
254 | } | 264 | } |
255 | 265 | ||
256 | - | 266 | + // Auxiliary path visitor for recursive directory structure copying. |
257 | - public static class DirectoryCopier extends SimpleFileVisitor<Path> { | 267 | + private static class DirectoryCopier extends SimpleFileVisitor<Path> { |
258 | private Path src; | 268 | private Path src; |
259 | private Path dst; | 269 | private Path dst; |
260 | private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING; | 270 | private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING; | ... | ... |
... | @@ -21,11 +21,13 @@ import org.onosproject.core.ApplicationId; | ... | @@ -21,11 +21,13 @@ import org.onosproject.core.ApplicationId; |
21 | 21 | ||
22 | import javax.ws.rs.Consumes; | 22 | import javax.ws.rs.Consumes; |
23 | import javax.ws.rs.DELETE; | 23 | import javax.ws.rs.DELETE; |
24 | +import javax.ws.rs.DefaultValue; | ||
24 | import javax.ws.rs.GET; | 25 | import javax.ws.rs.GET; |
25 | import javax.ws.rs.POST; | 26 | import javax.ws.rs.POST; |
26 | import javax.ws.rs.Path; | 27 | import javax.ws.rs.Path; |
27 | import javax.ws.rs.PathParam; | 28 | import javax.ws.rs.PathParam; |
28 | import javax.ws.rs.Produces; | 29 | import javax.ws.rs.Produces; |
30 | +import javax.ws.rs.QueryParam; | ||
29 | import javax.ws.rs.core.MediaType; | 31 | import javax.ws.rs.core.MediaType; |
30 | import javax.ws.rs.core.Response; | 32 | import javax.ws.rs.core.Response; |
31 | import java.io.InputStream; | 33 | import java.io.InputStream; |
... | @@ -55,9 +57,14 @@ public class ApplicationsWebResource extends AbstractWebResource { | ... | @@ -55,9 +57,14 @@ public class ApplicationsWebResource extends AbstractWebResource { |
55 | @POST | 57 | @POST |
56 | @Consumes(MediaType.APPLICATION_OCTET_STREAM) | 58 | @Consumes(MediaType.APPLICATION_OCTET_STREAM) |
57 | @Produces(MediaType.APPLICATION_JSON) | 59 | @Produces(MediaType.APPLICATION_JSON) |
58 | - public Response installApplication(InputStream stream) { | 60 | + public Response installApplication(@QueryParam("activate") |
61 | + @DefaultValue("false") boolean activate, | ||
62 | + InputStream stream) { | ||
59 | ApplicationAdminService service = get(ApplicationAdminService.class); | 63 | ApplicationAdminService service = get(ApplicationAdminService.class); |
60 | Application app = service.install(stream); | 64 | Application app = service.install(stream); |
65 | + if (activate) { | ||
66 | + service.activate(app.id()); | ||
67 | + } | ||
61 | return ok(codec(Application.class).encode(app, this)).build(); | 68 | return ok(codec(Application.class).encode(app, this)).build(); |
62 | } | 69 | } |
63 | 70 | ... | ... |
-
Please register or login to post a comment