blob: 161659f92a9b567237d844224f5c00fb307adb02 [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.app.impl;
17
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.apache.felix.scr.annotations.Service;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080024import org.apache.karaf.features.Feature;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080025import org.apache.karaf.features.FeaturesService;
26import org.onosproject.app.ApplicationAdminService;
27import org.onosproject.app.ApplicationEvent;
28import org.onosproject.app.ApplicationListener;
29import org.onosproject.app.ApplicationService;
30import org.onosproject.app.ApplicationState;
31import org.onosproject.app.ApplicationStore;
32import org.onosproject.app.ApplicationStoreDelegate;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070033import org.onosproject.event.AbstractListenerManager;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080034import org.onosproject.core.Application;
35import org.onosproject.core.ApplicationId;
Changhoon Yoonb856b812015-08-10 03:47:19 +090036import org.onosproject.security.Permission;
37import org.onosproject.security.SecurityUtil;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080038import org.slf4j.Logger;
39
40import java.io.InputStream;
41import java.util.Set;
42
43import static com.google.common.base.Preconditions.checkNotNull;
44import static org.onosproject.app.ApplicationEvent.Type.*;
Changhoon Yoonb856b812015-08-10 03:47:19 +090045import static org.onosproject.security.AppPermission.Type.*;
Changhoon Yoon541ef712015-05-23 17:18:34 +090046import static org.onosproject.security.AppGuard.checkPermission;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080047import static org.slf4j.LoggerFactory.getLogger;
48
49/**
50 * Implementation of the application management service.
51 */
52@Component(immediate = true)
53@Service
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070054public class ApplicationManager
55 extends AbstractListenerManager<ApplicationEvent, ApplicationListener>
56 implements ApplicationService, ApplicationAdminService {
Thomas Vachuska02aeb032015-01-06 22:36:30 -080057
58 private final Logger log = getLogger(getClass());
59
60 private static final String APP_ID_NULL = "Application ID cannot be null";
61
Thomas Vachuska02aeb032015-01-06 22:36:30 -080062 private final ApplicationStoreDelegate delegate = new InternalStoreDelegate();
63
64 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 protected ApplicationStore store;
66
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected FeaturesService featuresService;
69
Thomas Vachuska62f04a42015-04-22 14:38:34 -070070 private boolean initializing;
71
Thomas Vachuska02aeb032015-01-06 22:36:30 -080072 @Activate
73 public void activate() {
Thomas Vachuska02aeb032015-01-06 22:36:30 -080074 eventDispatcher.addSink(ApplicationEvent.class, listenerRegistry);
Thomas Vachuska62f04a42015-04-22 14:38:34 -070075
76 initializing = true;
Thomas Vachuska8dc1a692015-03-31 01:01:37 -070077 store.setDelegate(delegate);
Thomas Vachuska62f04a42015-04-22 14:38:34 -070078 initializing = false;
79
Thomas Vachuska02aeb032015-01-06 22:36:30 -080080 log.info("Started");
81 }
82
83 @Deactivate
84 public void deactivate() {
Thomas Vachuska02aeb032015-01-06 22:36:30 -080085 eventDispatcher.removeSink(ApplicationEvent.class);
Thomas Vachuska8dc1a692015-03-31 01:01:37 -070086 store.unsetDelegate(delegate);
Thomas Vachuska02aeb032015-01-06 22:36:30 -080087 log.info("Stopped");
88 }
89
90 @Override
91 public Set<Application> getApplications() {
Changhoon Yoonb856b812015-08-10 03:47:19 +090092 checkPermission(APP_READ);
Thomas Vachuska02aeb032015-01-06 22:36:30 -080093 return store.getApplications();
94 }
95
96 @Override
97 public ApplicationId getId(String name) {
Changhoon Yoonb856b812015-08-10 03:47:19 +090098 checkPermission(APP_READ);
Thomas Vachuska02aeb032015-01-06 22:36:30 -080099 checkNotNull(name, "Name cannot be null");
100 return store.getId(name);
101 }
102
103 @Override
104 public Application getApplication(ApplicationId appId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900105 checkPermission(APP_READ);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800106 checkNotNull(appId, APP_ID_NULL);
107 return store.getApplication(appId);
108 }
109
110 @Override
111 public ApplicationState getState(ApplicationId appId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900112 checkPermission(APP_READ);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800113 checkNotNull(appId, APP_ID_NULL);
114 return store.getState(appId);
115 }
116
117 @Override
118 public Set<Permission> getPermissions(ApplicationId appId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900119 checkPermission(APP_READ);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800120 checkNotNull(appId, APP_ID_NULL);
121 return store.getPermissions(appId);
122 }
123
124 @Override
125 public Application install(InputStream appDescStream) {
126 checkNotNull(appDescStream, "Application archive stream cannot be null");
Changhoon Yoonb856b812015-08-10 03:47:19 +0900127 Application app = store.create(appDescStream);
128 SecurityUtil.register(app.id());
129 return app;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800130 }
131
132 @Override
133 public void uninstall(ApplicationId appId) {
134 checkNotNull(appId, APP_ID_NULL);
135 try {
136 store.remove(appId);
137 } catch (Exception e) {
138 log.warn("Unable to purge application directory for {}", appId.name());
139 }
140 }
141
142 @Override
143 public void activate(ApplicationId appId) {
144 checkNotNull(appId, APP_ID_NULL);
Changhoon Yoonb856b812015-08-10 03:47:19 +0900145 if (!SecurityUtil.isAppSecured(appId)) {
146 return;
147 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800148 store.activate(appId);
149 }
150
151 @Override
152 public void deactivate(ApplicationId appId) {
153 checkNotNull(appId, APP_ID_NULL);
154 store.deactivate(appId);
155 }
156
157 @Override
158 public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
159 checkNotNull(appId, APP_ID_NULL);
160 checkNotNull(permissions, "Permissions cannot be null");
161 store.setPermissions(appId, permissions);
162 }
163
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800164 private class InternalStoreDelegate implements ApplicationStoreDelegate {
165 @Override
166 public void notify(ApplicationEvent event) {
167 ApplicationEvent.Type type = event.type();
168 Application app = event.subject();
169 try {
170 if (type == APP_ACTIVATED) {
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700171 if (installAppFeatures(app)) {
172 log.info("Application {} has been activated", app.id().name());
173 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800174
175 } else if (type == APP_DEACTIVATED) {
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700176 if (uninstallAppFeatures(app)) {
177 log.info("Application {} has been deactivated", app.id().name());
178 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800179
180 } else if (type == APP_INSTALLED) {
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700181 if (installAppArtifacts(app)) {
182 log.info("Application {} has been installed", app.id().name());
183 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800184
185 } else if (type == APP_UNINSTALLED) {
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700186 if (uninstallAppFeatures(app) || uninstallAppArtifacts(app)) {
187 log.info("Application {} has been uninstalled", app.id().name());
188 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800189
190 }
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700191 post(event);
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800192
193 } catch (Exception e) {
194 log.warn("Unable to perform operation on application " + app.id().name(), e);
195 }
196 }
197 }
198
Thomas Vachuska0d6af8d2015-03-23 15:54:47 -0700199 // The following methods are fully synchronized to guard against remote vs.
200 // locally induced feature service interactions.
201
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700202 private synchronized boolean installAppArtifacts(Application app) throws Exception {
203 if (app.featuresRepo().isPresent() &&
204 featuresService.getRepository(app.featuresRepo().get()) == null) {
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800205 featuresService.addRepository(app.featuresRepo().get());
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700206 return true;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800207 }
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700208 return false;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800209 }
210
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700211 private synchronized boolean uninstallAppArtifacts(Application app) throws Exception {
212 if (app.featuresRepo().isPresent() &&
213 featuresService.getRepository(app.featuresRepo().get()) != null) {
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800214 featuresService.removeRepository(app.featuresRepo().get());
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700215 return true;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800216 }
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700217 return false;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800218 }
219
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700220 private synchronized boolean installAppFeatures(Application app) throws Exception {
221 boolean changed = false;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800222 for (String name : app.features()) {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800223 Feature feature = featuresService.getFeature(name);
Thomas Vachuska62f04a42015-04-22 14:38:34 -0700224 if (feature != null && !featuresService.isInstalled(feature)) {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800225 featuresService.installFeature(name);
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700226 changed = true;
Thomas Vachuska62f04a42015-04-22 14:38:34 -0700227 } else if (feature == null && !initializing) {
228 // Suppress feature-not-found reporting during startup since these
229 // can arise naturally from the staggered cluster install.
230 log.warn("Feature {} not found", name);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800231 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800232 }
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700233 return changed;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800234 }
235
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700236 private synchronized boolean uninstallAppFeatures(Application app) throws Exception {
237 boolean changed = false;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800238 for (String name : app.features()) {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800239 Feature feature = featuresService.getFeature(name);
Thomas Vachuska62f04a42015-04-22 14:38:34 -0700240 if (feature != null && featuresService.isInstalled(feature)) {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800241 featuresService.uninstallFeature(name);
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700242 changed = true;
Thomas Vachuska62f04a42015-04-22 14:38:34 -0700243 } else if (feature == null) {
244 log.warn("Feature {} not found", name);
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800245 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800246 }
Thomas Vachuska9ff88a92015-05-13 13:57:18 -0700247 return changed;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800248 }
249
250}