blob: 31e6850e9c01cecef23e6cfe7f591652c815a52b [file] [log] [blame]
Pierre De Rop3a00a212015-03-01 09:27:46 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.dm.impl;
20
21import java.lang.reflect.InvocationTargetException;
22import java.util.Dictionary;
23import java.util.Properties;
24import java.util.concurrent.atomic.AtomicBoolean;
25
Pierre De Ropc40d93f2015-05-04 20:25:57 +000026import org.apache.felix.dm.Component;
Pierre De Rop3a00a212015-03-01 09:27:46 +000027import org.apache.felix.dm.ConfigurationDependency;
28import org.apache.felix.dm.Logger;
29import org.apache.felix.dm.PropertyMetaData;
30import org.apache.felix.dm.context.AbstractDependency;
31import org.apache.felix.dm.context.DependencyContext;
32import org.apache.felix.dm.context.Event;
33import org.apache.felix.dm.context.EventType;
34import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
35import org.osgi.framework.BundleContext;
36import org.osgi.framework.Constants;
37import org.osgi.framework.ServiceRegistration;
38import org.osgi.service.cm.ConfigurationException;
39import org.osgi.service.cm.ManagedService;
40
41/**
42 * Implementation for a configuration dependency.
43 *
44 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
45 */
46public class ConfigurationDependencyImpl extends AbstractDependency<ConfigurationDependency> implements ConfigurationDependency, ManagedService {
47 private Dictionary<String, Object> m_settings;
48 private String m_pid;
49 private ServiceRegistration m_registration;
50 private MetaTypeProviderImpl m_metaType;
51 private final AtomicBoolean m_updateInvokedCache = new AtomicBoolean();
52 private final Logger m_logger;
53 private final BundleContext m_context;
54
55 public ConfigurationDependencyImpl() {
56 this(null, null);
57 }
58
59 public ConfigurationDependencyImpl(BundleContext context, Logger logger) {
60 m_context = context;
61 m_logger = logger;
62 setRequired(true);
63 setCallback("updated");
64 }
65
66 public ConfigurationDependencyImpl(ConfigurationDependencyImpl prototype) {
67 super(prototype);
68 m_context = prototype.m_context;
69 m_pid = prototype.m_pid;
70 m_logger = prototype.m_logger;
71 m_metaType = prototype.m_metaType != null ? new MetaTypeProviderImpl(prototype.m_metaType, this, null) : null;
72 }
73
74 @Override
75 public Class<?> getAutoConfigType() {
76 return null; // we don't support auto config mode.
77 }
78
79 @Override
80 public DependencyContext createCopy() {
81 return new ConfigurationDependencyImpl(this);
82 }
83
84 public ConfigurationDependencyImpl setCallback(String callback) {
85 super.setCallbacks(callback, null);
86 return this;
87 }
88
89 public ConfigurationDependencyImpl setCallback(Object instance, String callback) {
90 super.setCallbacks(instance, callback, null);
91 return this;
92 }
93
94 @Override
95 public boolean needsInstance() {
Pierre De Rop7b5d1d12015-05-21 17:58:58 +000096 return m_callbackInstance == null;
Pierre De Rop3a00a212015-03-01 09:27:46 +000097 }
98
99 @Override
100 public void start() {
101 BundleContext context = m_component.getBundleContext();
102 if (context != null) { // If null, we are in a test environment
103 Properties props = new Properties();
104 props.put(Constants.SERVICE_PID, m_pid);
105 ManagedService ms = this;
106 if (m_metaType != null) {
107 ms = m_metaType;
108 }
109 m_registration = context.registerService(ManagedService.class.getName(), ms, props);
110 }
111 super.start();
112 }
113
114 @Override
115 public void stop() {
116 if (m_registration != null) {
117 try {
118 m_registration.unregister();
119 } catch (IllegalStateException e) {}
120 m_registration = null;
121 }
122 super.stop();
123 }
124
125 public ConfigurationDependency setPid(String pid) {
126 ensureNotActive();
127 m_pid = pid;
128 return this;
129 }
130
131 @Override
132 public String getSimpleName() {
133 return m_pid;
134 }
135
136 @Override
137 public String getFilter() {
138 return null;
139 }
140
141 public String getType() {
142 return "configuration";
143 }
144
145 public ConfigurationDependency add(PropertyMetaData properties)
146 {
147 createMetaTypeImpl();
148 m_metaType.add(properties);
149 return this;
150 }
151
152 public ConfigurationDependency setDescription(String description)
153 {
154 createMetaTypeImpl();
155 m_metaType.setDescription(description);
156 return this;
157 }
158
159 public ConfigurationDependency setHeading(String heading)
160 {
161 createMetaTypeImpl();
162 m_metaType.setName(heading);
163 return this;
164 }
165
166 public ConfigurationDependency setLocalization(String path)
167 {
168 createMetaTypeImpl();
169 m_metaType.setLocalization(path);
170 return this;
171 }
172
173 @SuppressWarnings("unchecked")
174 @Override
175 public Dictionary<String, Object> getProperties() {
176 if (m_settings == null) {
177 throw new IllegalStateException("cannot find configuration");
178 }
179 return m_settings;
180 }
181
182 @SuppressWarnings({"unchecked", "rawtypes"})
183 @Override
184 public void updated(Dictionary settings) throws ConfigurationException {
185 m_updateInvokedCache.set(false);
186 Dictionary<String, Object> oldSettings = null;
187 synchronized (this) {
188 oldSettings = m_settings;
189 }
190
191 if (oldSettings == null && settings == null) {
192 // CM has started but our configuration is not still present in the CM database: ignore
193 return;
194 }
195
196 // If this is initial settings, or a configuration update, we handle it synchronously.
197 // We'll conclude that the dependency is available only if invoking updated did not cause
198 // any ConfigurationException.
Pierre De Ropc8295c22015-06-04 10:15:35 +0000199 Object[] instances = m_component.getInstances();
200 if (instances != null) {
201 try {
202 invokeUpdated(settings);
203 } catch (ConfigurationException e) {
204 logConfigurationException(e);
205 throw e;
Pierre De Rop3a00a212015-03-01 09:27:46 +0000206 }
207 }
208
209 // At this point, we have accepted the configuration.
210 synchronized (this) {
211 m_settings = settings;
212 }
213
214 if ((oldSettings == null) && (settings != null)) {
215 // Notify the component that our dependency is available.
216 m_component.handleEvent(this, EventType.ADDED, new ConfigurationEventImpl(m_pid, settings));
217 }
218 else if ((oldSettings != null) && (settings != null)) {
219 // Notify the component that our dependency has changed.
220 m_component.handleEvent(this, EventType.CHANGED, new ConfigurationEventImpl(m_pid, settings));
221 }
222 else if ((oldSettings != null) && (settings == null)) {
223 // Notify the component that our dependency has been removed.
224 // Notice that the component will be stopped, and then all required dependencies will be unbound
225 // (including our configuration dependency).
226 m_component.handleEvent(this, EventType.REMOVED, new ConfigurationEventImpl(m_pid, oldSettings));
227 }
228 }
229
230 @Override
231 public void invokeCallback(EventType type, Event ... event) {
232 switch (type) {
233 case ADDED:
234 try {
235 invokeUpdated(m_settings);
236 } catch (ConfigurationException e) {
237 logConfigurationException(e);
238 }
239 break;
240 case CHANGED:
241 // We already did that synchronously, from our updated method
242 break;
243 case REMOVED:
244 // The state machine is stopping us. We have to invoke updated(null).
Pierre De Ropc8295c22015-06-04 10:15:35 +0000245 // Reset for the next time the state machine calls invokeAdd
246 m_updateInvokedCache.set(false);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000247 break;
248 default:
249 break;
250 }
251 }
252
253 private void invokeUpdated(Dictionary<?,?> settings) throws ConfigurationException {
254 if (m_updateInvokedCache.compareAndSet(false, true)) {
255 Object[] instances = super.getInstances(); // either the callback instance or the component instances
256 if (instances != null) {
257 for (int i = 0; i < instances.length; i++) {
258 try {
259 InvocationUtil.invokeCallbackMethod(instances[i],
260 m_add, new Class[][] {
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000261 { Dictionary.class },
262 { Component.class, Dictionary.class },
263 {} },
264 new Object[][] {
265 { settings },
266 { m_component, settings },
267 {} });
Pierre De Rop3a00a212015-03-01 09:27:46 +0000268 }
269
270 catch (InvocationTargetException e) {
271 // The component has thrown an exception during it's
272 // callback invocation.
273 if (e.getTargetException() instanceof ConfigurationException) {
274 // the callback threw an OSGi
275 // ConfigurationException: just re-throw it.
276 throw (ConfigurationException) e
277 .getTargetException();
278 } else {
279 // wrap the callback exception into a
280 // ConfigurationException.
281 throw new ConfigurationException(null,
282 "Configuration update failed",
283 e.getTargetException());
284 }
285 } catch (NoSuchMethodException e) {
286 // if the method does not exist, ignore it
287 } catch (Throwable t) {
288 // wrap any other exception as a ConfigurationException.
289 throw new ConfigurationException(null,
290 "Configuration update failed", t);
291 }
292 }
293 }
294 }
295 }
296
297 private synchronized void createMetaTypeImpl() {
298 if (m_metaType == null) {
299 m_metaType = new MetaTypeProviderImpl(m_pid, m_context, m_logger, this, null);
300 }
301 }
302
303 private void logConfigurationException(ConfigurationException e) {
304 if (m_logger != null) {
305 m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for pid " + m_pid, e);
306 }
307 }
308}