blob: 90e60051459748429032d7b06675642f4be8952b [file] [log] [blame]
Thomas Vachuska02aeb032015-01-06 22:36:30 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.common.app;
17
18import com.google.common.collect.ImmutableSet;
19import com.google.common.io.ByteStreams;
20import com.google.common.io.Files;
21import org.apache.commons.configuration.ConfigurationException;
22import org.apache.commons.configuration.XMLConfiguration;
23import org.onlab.util.Tools;
24import org.onosproject.app.ApplicationDescription;
25import org.onosproject.app.ApplicationEvent;
26import org.onosproject.app.ApplicationException;
27import org.onosproject.app.ApplicationStoreDelegate;
28import org.onosproject.app.DefaultApplicationDescription;
29import org.onosproject.core.Permission;
30import org.onosproject.core.Version;
31import org.onosproject.store.AbstractStore;
32import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
35import java.io.ByteArrayInputStream;
36import java.io.File;
37import java.io.FileInputStream;
38import java.io.FileNotFoundException;
39import java.io.IOException;
40import java.io.InputStream;
41import java.net.URI;
42import java.util.Set;
43import java.util.zip.ZipEntry;
44import java.util.zip.ZipInputStream;
45
46import static com.google.common.io.ByteStreams.toByteArray;
47import static com.google.common.io.Files.createParentDirs;
48import static com.google.common.io.Files.write;
49
50/**
51 * Facility for reading application archive stream and managing application
52 * directory structure.
53 */
54public class ApplicationArchive
55 extends AbstractStore<ApplicationEvent, ApplicationStoreDelegate> {
56
57 private static final String NAME = "[@name]";
58 private static final String ORIGIN = "[@origin]";
59 private static final String VERSION = "[@version]";
60 private static final String FEATURES_REPO = "[@featuresRepo]";
61 private static final String FEATURES = "[@features]";
62 private static final String DESCRIPTION = "description";
63
64 private static Logger log = LoggerFactory.getLogger(ApplicationArchive.class);
65 private static final String APP_XML = "app.xml";
66 private static final String APPS_ROOT = "data/apps/";
67
68 private File appsDir = new File(APPS_ROOT);
69
70 /**
71 * Sets the root directory where application artifacts are kept.
72 *
73 * @param appsRoot top-level applications directory path
74 */
75 protected void setAppsRoot(String appsRoot) {
76 this.appsDir = new File(appsRoot);
77 }
78
79 /**
80 * Returns the root directory where application artifacts are kept.
81 *
82 * @return top-level applications directory path
83 */
84 protected String getAppsRoot() {
85 return appsDir.getPath();
86 }
87
88 /**
89 * Returns the set of installed application names.
90 *
91 * @return installed application names
92 */
93 public Set<String> getApplicationNames() {
94 ImmutableSet.Builder<String> names = ImmutableSet.builder();
95 File[] files = appsDir.listFiles(File::isDirectory);
96 if (files != null) {
97 for (File file : files) {
98 names.add(file.getName());
99 }
100 }
101 return names.build();
102 }
103
104 /**
105 * Loads the application descriptor from the specified application archive
106 * stream and saves the stream in the appropriate application archive
107 * directory.
108 *
109 * @param appName application name
110 * @return application descriptor
111 * @throws org.onosproject.app.ApplicationException if unable to read application description
112 */
113 public ApplicationDescription getApplicationDescription(String appName) {
114 try {
115 return loadAppDescription(new XMLConfiguration(appFile(appName, APP_XML)));
116 } catch (Exception e) {
117 throw new ApplicationException("Unable to get app description", e);
118 }
119 }
120
121 /**
122 * Loads the application descriptor from the specified application archive
123 * stream and saves the stream in the appropriate application archive
124 * directory.
125 *
126 * @param stream application archive stream
127 * @return application descriptor
128 * @throws org.onosproject.app.ApplicationException if unable to read the
129 * archive stream or store
130 * the application archive
131 */
132 public ApplicationDescription saveApplication(InputStream stream) {
133 try (InputStream ais = stream) {
134 byte[] cache = toByteArray(ais);
135 InputStream bis = new ByteArrayInputStream(cache);
136 ApplicationDescription desc = parseAppDescription(bis);
137 bis.reset();
138
139 expandApplication(bis, desc);
140 bis.reset();
141
142 saveApplication(bis, desc);
143 installArtifacts(desc);
144 return desc;
145 } catch (IOException e) {
146 throw new ApplicationException("Unable to save application", e);
147 }
148 }
149
150 /**
151 * Purges the application archive directory.
152 *
153 * @param appName application name
154 */
155 public void purgeApplication(String appName) {
156 try {
157 Tools.removeDirectory(new File(appsDir, appName));
158 } catch (IOException e) {
159 throw new ApplicationException("Unable to purge application " + appName, e);
160 }
161 }
162
163 /**
164 * Returns application archive stream for the specified application.
165 *
166 * @param appName application name
167 * @return application archive stream
168 */
169 public InputStream getApplicationInputStream(String appName) {
170 try {
171 return new FileInputStream(appFile(appName, appName + ".zip"));
172 } catch (FileNotFoundException e) {
173 throw new ApplicationException("Application " + appName + " not found");
174 }
175 }
176
177 // Scans the specified ZIP stream for app.xml entry and parses it producing
178 // an application descriptor.
179 private ApplicationDescription parseAppDescription(InputStream stream)
180 throws IOException {
181 try (ZipInputStream zis = new ZipInputStream(stream)) {
182 ZipEntry entry;
183 while ((entry = zis.getNextEntry()) != null) {
184 if (entry.getName().equals(APP_XML)) {
185 byte[] data = new byte[(int) entry.getSize()];
186 ByteStreams.readFully(zis, data);
187 XMLConfiguration cfg = new XMLConfiguration();
188 try {
189 cfg.load(new ByteArrayInputStream(data));
190 return loadAppDescription(cfg);
191 } catch (ConfigurationException e) {
192 throw new IOException("Unable to parse " + APP_XML, e);
193 }
194 }
195 zis.closeEntry();
196 }
197 }
198 throw new IOException("Unable to locate " + APP_XML);
199 }
200
201 private ApplicationDescription loadAppDescription(XMLConfiguration cfg) {
202 cfg.setAttributeSplittingDisabled(true);
203 String name = cfg.getString(NAME);
204 Version version = Version.version(cfg.getString(VERSION));
205 String desc = cfg.getString(DESCRIPTION);
206 String origin = cfg.getString(ORIGIN);
207 Set<Permission> perms = ImmutableSet.of();
208 String featRepo = cfg.getString(FEATURES_REPO);
209 URI featuresRepo = featRepo != null ? URI.create(featRepo) : null;
210 Set<String> features = ImmutableSet.copyOf(cfg.getString(FEATURES).split(","));
211
212 return new DefaultApplicationDescription(name, version, desc, origin,
213 perms, featuresRepo, features);
214 }
215
216 // Expands the specified ZIP stream into app-specific directory.
217 private void expandApplication(InputStream stream, ApplicationDescription desc)
218 throws IOException {
219 ZipInputStream zis = new ZipInputStream(stream);
220 ZipEntry entry;
221 File appDir = new File(appsDir, desc.name());
222 while ((entry = zis.getNextEntry()) != null) {
223 byte[] data = new byte[(int) entry.getSize()];
224 ByteStreams.readFully(zis, data);
225 zis.closeEntry();
226
227 File file = new File(appDir, entry.getName());
228 createParentDirs(file);
229 write(data, file);
230 }
231 zis.close();
232 }
233
234 // Saves the specified ZIP stream into a file under app-specific directory.
235 private void saveApplication(InputStream stream, ApplicationDescription desc)
236 throws IOException {
237 Files.write(toByteArray(stream), appFile(desc.name(), desc.name() + ".zip"));
238 }
239
240 // Installs application artifacts into M2 repository.
241 private void installArtifacts(ApplicationDescription desc) {
242 // FIXME: implement M2 repository copy
243 }
244
245 protected boolean setActive(String appName) {
246 try {
247 return appFile(appName, "active").createNewFile();
248 } catch (IOException e) {
249 throw new ApplicationException("Unable to mark app as active", e);
250 }
251 }
252
253 protected boolean clearActive(String appName) {
254 return appFile(appName, "active").delete();
255 }
256
257 protected boolean isActive(String appName) {
258 return appFile(appName, "active").exists();
259 }
260
261
262 // Returns the name of the file located under the specified app directory.
263 private File appFile(String appName, String fileName) {
264 return new File(new File(appsDir, appName), fileName);
265 }
266
267}