blob: e1224c40d290fc29e7d96357a3f6243d8593ab15 [file] [log] [blame]
Thomas Vachuska02aeb032015-01-06 22:36:30 -08001/*
Jian Lic35415d2016-01-14 17:22:31 -08002 * Copyright 2015-2016 Open Networking Laboratory
Thomas Vachuska02aeb032015-01-06 22:36:30 -08003 *
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
Thomas Vachuskaebf5e542015-02-03 19:38:13 -080018import com.google.common.collect.ImmutableList;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080019import com.google.common.collect.ImmutableSet;
Thomas Vachuska761f0042015-11-11 19:10:17 -080020import com.google.common.collect.Lists;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080021import com.google.common.io.ByteStreams;
22import com.google.common.io.Files;
23import org.apache.commons.configuration.ConfigurationException;
Changhoon Yoonb856b812015-08-10 03:47:19 +090024import org.apache.commons.configuration.HierarchicalConfiguration;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080025import org.apache.commons.configuration.XMLConfiguration;
Jian Lic35415d2016-01-14 17:22:31 -080026import org.apache.commons.lang.StringUtils;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080027import org.onlab.util.Tools;
28import org.onosproject.app.ApplicationDescription;
29import org.onosproject.app.ApplicationEvent;
30import org.onosproject.app.ApplicationException;
31import org.onosproject.app.ApplicationStoreDelegate;
32import org.onosproject.app.DefaultApplicationDescription;
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +090033import org.onosproject.core.ApplicationRole;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080034import org.onosproject.core.Version;
Changhoon Yoonb856b812015-08-10 03:47:19 +090035import org.onosproject.security.AppPermission;
36import org.onosproject.security.Permission;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080037import org.onosproject.store.AbstractStore;
38import org.slf4j.Logger;
39import org.slf4j.LoggerFactory;
40
Jian Lic35415d2016-01-14 17:22:31 -080041import javax.imageio.ImageIO;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080042import java.io.ByteArrayInputStream;
Jian Li97d6b2d2016-01-20 10:13:43 -080043import java.io.ByteArrayOutputStream;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080044import java.io.File;
45import java.io.FileInputStream;
46import java.io.FileNotFoundException;
47import java.io.IOException;
48import java.io.InputStream;
49import java.net.URI;
HIGUCHI Yuta436f8d52015-12-07 21:17:48 -080050import java.nio.charset.StandardCharsets;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080051import java.nio.file.NoSuchFileException;
Thomas Vachuskaebf5e542015-02-03 19:38:13 -080052import java.util.List;
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +090053import java.util.Locale;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080054import java.util.Set;
55import java.util.zip.ZipEntry;
56import java.util.zip.ZipInputStream;
57
Thomas Vachuskae18a3302015-06-23 12:48:28 -070058import static com.google.common.base.Preconditions.checkState;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080059import static com.google.common.io.ByteStreams.toByteArray;
60import static com.google.common.io.Files.createParentDirs;
61import static com.google.common.io.Files.write;
62
63/**
64 * Facility for reading application archive stream and managing application
65 * directory structure.
66 */
67public class ApplicationArchive
68 extends AbstractStore<ApplicationEvent, ApplicationStoreDelegate> {
69
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -070070 private static Logger log = LoggerFactory.getLogger(ApplicationArchive.class);
71
Thomas Vachuska62ad95f2015-02-18 12:11:36 -080072 // Magic strings to search for at the beginning of the archive stream
73 private static final String XML_MAGIC = "<?xml ";
Brian O'Connore4a4f992016-04-06 22:50:31 -070074 private static final String ZIP_MAGIC = "PK";
Thomas Vachuska62ad95f2015-02-18 12:11:36 -080075
76 // Magic strings to search for and how deep to search it into the archive stream
77 private static final String APP_MAGIC = "<app ";
78 private static final int APP_MAGIC_DEPTH = 1024;
79
Thomas Vachuska02aeb032015-01-06 22:36:30 -080080 private static final String NAME = "[@name]";
81 private static final String ORIGIN = "[@origin]";
82 private static final String VERSION = "[@version]";
83 private static final String FEATURES_REPO = "[@featuresRepo]";
84 private static final String FEATURES = "[@features]";
Thomas Vachuska761f0042015-11-11 19:10:17 -080085 private static final String APPS = "[@apps]";
Thomas Vachuska02aeb032015-01-06 22:36:30 -080086 private static final String DESCRIPTION = "description";
87
Jian Li01b0f5952016-01-20 11:02:07 -080088 private static final String UTILITY = "utility";
89
Jian Lic35415d2016-01-14 17:22:31 -080090 private static final String CATEGORY = "[@category]";
91 private static final String URL = "[@url]";
Simon Huntafae2f72016-03-04 21:18:23 -080092 private static final String TITLE = "[@title]";
Jian Lic35415d2016-01-14 17:22:31 -080093
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +090094 private static final String ROLE = "security.role";
Changhoon Yoonb856b812015-08-10 03:47:19 +090095 private static final String APP_PERMISSIONS = "security.permissions.app-perm";
96 private static final String NET_PERMISSIONS = "security.permissions.net-perm";
97 private static final String JAVA_PERMISSIONS = "security.permissions.java-perm";
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +090098
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -070099 private static final String OAR = ".oar";
Jian Li97d6b2d2016-01-20 10:13:43 -0800100 private static final String PNG = "png";
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800101 private static final String APP_XML = "app.xml";
Jian Li97d6b2d2016-01-20 10:13:43 -0800102 private static final String APP_PNG = "app.png";
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800103 private static final String M2_PREFIX = "m2";
104
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700105 private static final String ROOT = "../";
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800106 private static final String M2_ROOT = "system/";
Thomas Vachuskad5d9bcb2015-03-18 17:46:20 -0700107 private static final String APPS_ROOT = "apps/";
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800108
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700109 private File root = new File(ROOT);
110 private File appsDir = new File(root, APPS_ROOT);
111 private File m2Dir = new File(M2_ROOT);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800112
113 /**
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700114 * Sets the root directory where apps directory is contained.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800115 *
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700116 * @param root top-level directory path
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800117 */
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700118 protected void setRootPath(String root) {
119 this.root = new File(root);
120 this.appsDir = new File(this.root, APPS_ROOT);
121 this.m2Dir = new File(M2_ROOT);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800122 }
123
124 /**
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700125 * Returns the root directory where apps directory is contained.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800126 *
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700127 * @return top-level directory path
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800128 */
Thomas Vachuskae18a3302015-06-23 12:48:28 -0700129 public String getRootPath() {
Thomas Vachuska40a398b2015-04-03 22:26:30 -0700130 return root.getPath();
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800131 }
132
133 /**
134 * Returns the set of installed application names.
135 *
136 * @return installed application names
137 */
138 public Set<String> getApplicationNames() {
139 ImmutableSet.Builder<String> names = ImmutableSet.builder();
140 File[] files = appsDir.listFiles(File::isDirectory);
141 if (files != null) {
142 for (File file : files) {
143 names.add(file.getName());
144 }
145 }
146 return names.build();
147 }
148
149 /**
Thomas Vachuskacf960112015-03-06 22:36:51 -0800150 * Returns the timestamp in millis since start of epoch, of when the
151 * specified application was last modified or changed state.
152 *
153 * @param appName application name
154 * @return number of millis since start of epoch
155 */
156 public long getUpdateTime(String appName) {
157 return appFile(appName, APP_XML).lastModified();
158 }
159
160 /**
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800161 * Loads the application descriptor from the specified application archive
162 * stream and saves the stream in the appropriate application archive
163 * directory.
164 *
165 * @param appName application name
166 * @return application descriptor
167 * @throws org.onosproject.app.ApplicationException if unable to read application description
168 */
169 public ApplicationDescription getApplicationDescription(String appName) {
170 try {
Thomas Vachuskaad35c342015-06-11 17:25:36 -0700171 XMLConfiguration cfg = new XMLConfiguration();
172 cfg.setAttributeSplittingDisabled(true);
173 cfg.setDelimiterParsingDisabled(true);
174 cfg.load(appFile(appName, APP_XML));
175 return loadAppDescription(cfg);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800176 } catch (Exception e) {
177 throw new ApplicationException("Unable to get app description", e);
178 }
179 }
180
181 /**
182 * Loads the application descriptor from the specified application archive
183 * stream and saves the stream in the appropriate application archive
184 * directory.
185 *
186 * @param stream application archive stream
187 * @return application descriptor
188 * @throws org.onosproject.app.ApplicationException if unable to read the
189 * archive stream or store
190 * the application archive
191 */
Thomas Vachuska0249b532015-02-20 16:46:18 -0800192 public synchronized ApplicationDescription saveApplication(InputStream stream) {
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800193 try (InputStream ais = stream) {
194 byte[] cache = toByteArray(ais);
195 InputStream bis = new ByteArrayInputStream(cache);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800196
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800197 boolean plainXml = isPlainXml(cache);
198 ApplicationDescription desc = plainXml ?
199 parsePlainAppDescription(bis) : parseZippedAppDescription(bis);
Thomas Vachuskae18a3302015-06-23 12:48:28 -0700200 checkState(!appFile(desc.name(), APP_XML).exists(),
Jian Li97d6b2d2016-01-20 10:13:43 -0800201 "Application %s already installed", desc.name());
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800202
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800203 if (plainXml) {
204 expandPlainApplication(cache, desc);
205 } else {
206 bis.reset();
207 expandZippedApplication(bis, desc);
208
209 bis.reset();
210 saveApplication(bis, desc);
211 }
212
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800213 installArtifacts(desc);
214 return desc;
215 } catch (IOException e) {
216 throw new ApplicationException("Unable to save application", e);
217 }
218 }
219
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800220 // Indicates whether the stream encoded in the given bytes is plain XML.
221 private boolean isPlainXml(byte[] bytes) {
Brian O'Connore4a4f992016-04-06 22:50:31 -0700222 return !substring(bytes, ZIP_MAGIC.length()).equals(ZIP_MAGIC) &&
223 (substring(bytes, XML_MAGIC.length()).equals(XML_MAGIC) ||
224 substring(bytes, APP_MAGIC_DEPTH).contains(APP_MAGIC));
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800225 }
226
227 // Returns the substring of maximum possible length from the specified bytes.
228 private String substring(byte[] bytes, int length) {
HIGUCHI Yuta436f8d52015-12-07 21:17:48 -0800229 return new String(bytes, 0, Math.min(bytes.length, length), StandardCharsets.UTF_8);
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800230 }
231
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800232 /**
233 * Purges the application archive directory.
234 *
235 * @param appName application name
236 */
Thomas Vachuska0249b532015-02-20 16:46:18 -0800237 public synchronized void purgeApplication(String appName) {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800238 File appDir = new File(appsDir, appName);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800239 try {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800240 Tools.removeDirectory(appDir);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800241 } catch (IOException e) {
242 throw new ApplicationException("Unable to purge application " + appName, e);
243 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800244 if (appDir.exists()) {
245 throw new ApplicationException("Unable to purge application " + appName);
246 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800247 }
248
249 /**
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800250 * Returns application archive stream for the specified application. This
251 * will be either the application ZIP file or the application XML file.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800252 *
253 * @param appName application name
254 * @return application archive stream
255 */
Thomas Vachuska0249b532015-02-20 16:46:18 -0800256 public synchronized InputStream getApplicationInputStream(String appName) {
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800257 try {
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -0700258 File appFile = appFile(appName, appName + OAR);
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800259 return new FileInputStream(appFile.exists() ? appFile : appFile(appName, APP_XML));
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800260 } catch (FileNotFoundException e) {
261 throw new ApplicationException("Application " + appName + " not found");
262 }
263 }
264
265 // Scans the specified ZIP stream for app.xml entry and parses it producing
266 // an application descriptor.
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800267 private ApplicationDescription parseZippedAppDescription(InputStream stream)
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800268 throws IOException {
269 try (ZipInputStream zis = new ZipInputStream(stream)) {
270 ZipEntry entry;
271 while ((entry = zis.getNextEntry()) != null) {
272 if (entry.getName().equals(APP_XML)) {
Thomas Vachuskaebf5e542015-02-03 19:38:13 -0800273 byte[] data = ByteStreams.toByteArray(zis);
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800274 return parsePlainAppDescription(new ByteArrayInputStream(data));
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800275 }
276 zis.closeEntry();
277 }
278 }
279 throw new IOException("Unable to locate " + APP_XML);
280 }
281
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800282 // Scans the specified XML stream and parses it producing an application descriptor.
283 private ApplicationDescription parsePlainAppDescription(InputStream stream)
284 throws IOException {
285 XMLConfiguration cfg = new XMLConfiguration();
Thomas Vachuskaad35c342015-06-11 17:25:36 -0700286 cfg.setAttributeSplittingDisabled(true);
287 cfg.setDelimiterParsingDisabled(true);
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800288 try {
289 cfg.load(stream);
290 return loadAppDescription(cfg);
291 } catch (ConfigurationException e) {
292 throw new IOException("Unable to parse " + APP_XML, e);
293 }
294 }
295
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800296 private ApplicationDescription loadAppDescription(XMLConfiguration cfg) {
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800297 String name = cfg.getString(NAME);
298 Version version = Version.version(cfg.getString(VERSION));
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800299 String origin = cfg.getString(ORIGIN);
Simon Huntafae2f72016-03-04 21:18:23 -0800300
301 String title = cfg.getString(TITLE);
302 // FIXME: title should be set as attribute to APP, but fallback for now...
303 title = title == null ? name : title;
304
Jian Li01b0f5952016-01-20 11:02:07 -0800305 String category = cfg.getString(CATEGORY, UTILITY);
Jian Lic35415d2016-01-14 17:22:31 -0800306 String url = cfg.getString(URL);
307 byte[] icon = getApplicationIcon(name);
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +0900308 ApplicationRole role = getRole(cfg.getString(ROLE));
309 Set<Permission> perms = getPermissions(cfg);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800310 String featRepo = cfg.getString(FEATURES_REPO);
311 URI featuresRepo = featRepo != null ? URI.create(featRepo) : null;
Thomas Vachuskaad35c342015-06-11 17:25:36 -0700312 List<String> features = ImmutableList.copyOf(cfg.getString(FEATURES).split(","));
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800313
Thomas Vachuska761f0042015-11-11 19:10:17 -0800314 String apps = cfg.getString(APPS, "");
315 List<String> requiredApps = apps.isEmpty() ?
316 ImmutableList.of() : ImmutableList.copyOf(apps.split(","));
317
Jian Li8bcb4f22016-01-20 10:36:18 -0800318 // put full description to readme field
319 String readme = cfg.getString(DESCRIPTION);
Jian Lic35415d2016-01-14 17:22:31 -0800320
Jian Li8bcb4f22016-01-20 10:36:18 -0800321 // put short description to description field
322 String desc = compactDescription(readme);
Jian Lic35415d2016-01-14 17:22:31 -0800323
Simon Huntafae2f72016-03-04 21:18:23 -0800324 return new DefaultApplicationDescription(name, version, title, desc, origin,
Jian Li8bcb4f22016-01-20 10:36:18 -0800325 category, url, readme, icon,
326 role, perms, featuresRepo,
327 features, requiredApps);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800328 }
329
330 // Expands the specified ZIP stream into app-specific directory.
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800331 private void expandZippedApplication(InputStream stream, ApplicationDescription desc)
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800332 throws IOException {
333 ZipInputStream zis = new ZipInputStream(stream);
334 ZipEntry entry;
335 File appDir = new File(appsDir, desc.name());
336 while ((entry = zis.getNextEntry()) != null) {
Thomas Vachuskaebf5e542015-02-03 19:38:13 -0800337 if (!entry.isDirectory()) {
338 byte[] data = ByteStreams.toByteArray(zis);
339 zis.closeEntry();
Thomas Vachuskaebf5e542015-02-03 19:38:13 -0800340 File file = new File(appDir, entry.getName());
341 createParentDirs(file);
342 write(data, file);
343 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800344 }
345 zis.close();
346 }
347
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800348 // Saves the specified XML stream into app-specific directory.
349 private void expandPlainApplication(byte[] stream, ApplicationDescription desc)
350 throws IOException {
351 File file = appFile(desc.name(), APP_XML);
Thomas Vachuskae18a3302015-06-23 12:48:28 -0700352 checkState(!file.getParentFile().exists(), "Application already installed");
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800353 createParentDirs(file);
354 write(stream, file);
355 }
356
357
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800358 // Saves the specified ZIP stream into a file under app-specific directory.
359 private void saveApplication(InputStream stream, ApplicationDescription desc)
360 throws IOException {
Thomas Vachuskaa7a0f562015-04-14 23:27:44 -0700361 Files.write(toByteArray(stream), appFile(desc.name(), desc.name() + OAR));
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800362 }
363
364 // Installs application artifacts into M2 repository.
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800365 private void installArtifacts(ApplicationDescription desc) throws IOException {
366 try {
367 Tools.copyDirectory(appFile(desc.name(), M2_PREFIX), m2Dir);
368 } catch (NoSuchFileException e) {
369 log.debug("Application {} has no M2 artifacts", desc.name());
370 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800371 }
372
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800373 /**
374 * Marks the app as active by creating token file in the app directory.
375 *
376 * @param appName application name
377 * @return true if file was created
378 */
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800379 protected boolean setActive(String appName) {
380 try {
Thomas Vachuskae965b3d2016-03-03 11:42:48 -0800381 File active = appFile(appName, "active");
382 createParentDirs(active);
383 return active.createNewFile() && updateTime(appName);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800384 } catch (IOException e) {
Thomas Vachuskae965b3d2016-03-03 11:42:48 -0800385 log.warn("Unable to mark app {} as active", appName, e);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800386 throw new ApplicationException("Unable to mark app as active", e);
387 }
388 }
389
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800390 /**
391 * Clears the app as active by deleting token file in the app directory.
392 *
393 * @param appName application name
394 * @return true if file was deleted
395 */
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800396 protected boolean clearActive(String appName) {
Thomas Vachuskacf960112015-03-06 22:36:51 -0800397 return appFile(appName, "active").delete() && updateTime(appName);
398 }
399
400 /**
401 * Updates the time-stamp of the app descriptor file.
402 *
403 * @param appName application name
404 * @return true if the app descriptor was updated
405 */
Thomas Vachuska161baf52015-03-27 16:15:39 -0700406 protected boolean updateTime(String appName) {
Thomas Vachuskacf960112015-03-06 22:36:51 -0800407 return appFile(appName, APP_XML).setLastModified(System.currentTimeMillis());
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800408 }
409
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800410 /**
411 * Indicates whether the app was marked as active by checking for token file.
412 *
413 * @param appName application name
414 * @return true if the app is marked as active
415 */
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800416 protected boolean isActive(String appName) {
417 return appFile(appName, "active").exists();
418 }
419
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800420 // Returns the name of the file located under the specified app directory.
421 private File appFile(String appName, String fileName) {
422 return new File(new File(appsDir, appName), fileName);
423 }
424
Jian Li97d6b2d2016-01-20 10:13:43 -0800425 // Returns the icon file located under the specified app directory.
426 private File iconFile(String appName, String fileName) {
427 return new File(new File(appsDir, appName), fileName);
428 }
429
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +0900430 // Returns the set of Permissions specified in the app.xml file
431 private ImmutableSet<Permission> getPermissions(XMLConfiguration cfg) {
Thomas Vachuska761f0042015-11-11 19:10:17 -0800432 List<Permission> permissionList = Lists.newArrayList();
Changhoon Yoonb856b812015-08-10 03:47:19 +0900433
434 for (Object o : cfg.getList(APP_PERMISSIONS)) {
Changhoon Yoona7841ed2015-05-15 02:51:08 +0900435 String name = (String) o;
Changhoon Yoonb856b812015-08-10 03:47:19 +0900436 permissionList.add(new Permission(AppPermission.class.getName(), name));
437 }
438 for (Object o : cfg.getList(NET_PERMISSIONS)) {
439 //TODO: TO BE FLESHED OUT WHEN NETWORK PERMISSIONS ARE SUPPORTED
440 break;
441 }
442
443 List<HierarchicalConfiguration> fields =
444 cfg.configurationsAt(JAVA_PERMISSIONS);
445 for (HierarchicalConfiguration sub : fields) {
446 String classname = sub.getString("classname");
447 String name = sub.getString("name");
448 String actions = sub.getString("actions");
449
450 if (classname != null && name != null) {
451 permissionList.add(new Permission(classname, name, actions));
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +0900452 }
453 }
Changhoon Yoona7841ed2015-05-15 02:51:08 +0900454 return ImmutableSet.copyOf(permissionList);
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +0900455 }
456
Jian Lic35415d2016-01-14 17:22:31 -0800457 // Returns the byte stream from icon.png file in oar application archive.
458 private byte[] getApplicationIcon(String appName) {
Jian Li97d6b2d2016-01-20 10:13:43 -0800459
460 byte[] icon = new byte[0];
461 File iconFile = iconFile(appName, APP_PNG);
Jian Lic35415d2016-01-14 17:22:31 -0800462
463 if (!iconFile.exists()) {
Jian Li97d6b2d2016-01-20 10:13:43 -0800464 // assume that we can always fallback to default icon
465 iconFile = new File(appsDir, APP_PNG);
Jian Lic35415d2016-01-14 17:22:31 -0800466 }
467
Jian Lic35415d2016-01-14 17:22:31 -0800468 try {
Jian Li97d6b2d2016-01-20 10:13:43 -0800469 ByteArrayOutputStream bos = new ByteArrayOutputStream();
470 ImageIO.write(ImageIO.read(iconFile), PNG, bos);
471 icon = bos.toByteArray();
472 bos.close();
Jian Lic35415d2016-01-14 17:22:31 -0800473 } catch (IOException e) {
474 e.printStackTrace();
475 }
476
Jian Li97d6b2d2016-01-20 10:13:43 -0800477 return icon;
Jian Lic35415d2016-01-14 17:22:31 -0800478 }
479
Changhoon Yoonbdeb88a2015-05-12 20:35:31 +0900480 // Returns application role type
481 public ApplicationRole getRole(String value) {
482 if (value == null) {
483 return ApplicationRole.UNSPECIFIED;
484 } else {
485 try {
486 return ApplicationRole.valueOf(value.toUpperCase(Locale.ENGLISH));
487 } catch (IllegalArgumentException e) {
488 log.debug("Unknown role value: %s", value);
489 return ApplicationRole.UNSPECIFIED;
490 }
491 }
492 }
Jian Lic35415d2016-01-14 17:22:31 -0800493
494 // Returns the first sentence of the given sentence
495 private String compactDescription(String sentence) {
496 if (StringUtils.isNotEmpty(sentence)) {
497 if (StringUtils.contains(sentence, ".")) {
498 return StringUtils.substringBefore(sentence, ".") + ".";
499 } else {
Jian Li8bcb4f22016-01-20 10:36:18 -0800500 return sentence;
Jian Lic35415d2016-01-14 17:22:31 -0800501 }
502 }
503 return sentence;
504 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800505}