blob: f90df0cba5386e6ce513bc941ab937eeb5ad5e59 [file] [log] [blame]
Marcel Offermansa962bc92009-11-21 17:59:33 +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 */
Pierre De Ropbd642a62009-12-04 22:50:31 +000019package org.apache.felix.dm.impl.dependencies;
Marcel Offermanse14b3422009-11-25 23:04:32 +000020
Marcel Offermansa962bc92009-11-21 17:59:33 +000021import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
Marcel Offermanscae61362009-12-01 08:37:10 +000023import java.util.ArrayList;
Marcel Offermansa962bc92009-11-21 17:59:33 +000024import java.util.Dictionary;
Marcel Offermanscae61362009-12-01 08:37:10 +000025import java.util.HashSet;
26import java.util.List;
Marcel Offermansa962bc92009-11-21 17:59:33 +000027import java.util.Properties;
Marcel Offermanscae61362009-12-01 08:37:10 +000028import java.util.Set;
Marcel Offermansa962bc92009-11-21 17:59:33 +000029
Pierre De Ropbd642a62009-12-04 22:50:31 +000030import org.apache.felix.dm.dependencies.ConfigurationDependency;
Pierre De Ropbd642a62009-12-04 22:50:31 +000031import org.apache.felix.dm.impl.Logger;
32import org.apache.felix.dm.management.ServiceComponentDependency;
Marcel Offermansa962bc92009-11-21 17:59:33 +000033import org.osgi.framework.BundleContext;
34import org.osgi.framework.Constants;
35import org.osgi.framework.ServiceRegistration;
36import org.osgi.service.cm.ConfigurationException;
37import org.osgi.service.cm.ManagedService;
38
39/**
40 * Configuration dependency that can track the availability of a (valid) configuration.
41 * To use it, specify a PID for the configuration. The dependency is always required,
42 * because if it is not, it does not make sense to use the dependency manager. In that
43 * scenario, simply register your service as a <code>ManagedService(Factory)</code> and
44 * handle everything yourself. Also, only managed services are supported, not factories.
45 * There are a couple of things you need to be aware of when implementing the
46 * <code>updated(Dictionary)</code> method:
47 * <ul>
48 * <li>Make sure it throws a <code>ConfigurationException</code> when you get a
49 * configuration that is invalid. In this case, the dependency will not change:
50 * if it was not available, it will still not be. If it was available, it will
51 * remain available and implicitly assume you keep working with your old
52 * configuration.</li>
53 * <li>This method will be called before all required dependencies are available.
54 * Make sure you do not depend on these to parse your settings.</li>
55 * </ul>
56 *
57 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
58 */
Pierre De Ropbd642a62009-12-04 22:50:31 +000059public class ConfigurationDependencyImpl implements ConfigurationDependency, ManagedService, ServiceComponentDependency, DependencyActivation {
Marcel Offermansa962bc92009-11-21 17:59:33 +000060 private BundleContext m_context;
61 private String m_pid;
62 private ServiceRegistration m_registration;
Marcel Offermanscae61362009-12-01 08:37:10 +000063 protected List m_services = new ArrayList();
Marcel Offermansa962bc92009-11-21 17:59:33 +000064 private Dictionary m_settings;
65 private boolean m_propagate;
66 private final Logger m_logger;
67 private String m_callback;
Marcel Offermanscae61362009-12-01 08:37:10 +000068 private boolean m_isStarted;
69 private final Set m_updateInvokedCache = new HashSet();
Marcel Offermansa962bc92009-11-21 17:59:33 +000070
Pierre De Ropbd642a62009-12-04 22:50:31 +000071 public ConfigurationDependencyImpl(BundleContext context, Logger logger) {
Marcel Offermansa962bc92009-11-21 17:59:33 +000072 m_context = context;
73 m_logger = logger;
74 }
75
76 public synchronized boolean isAvailable() {
77 return m_settings != null;
78 }
79
80 /**
81 * Will always return <code>true</code> as optional configuration dependencies
82 * do not make sense. You might as well just implement <code>ManagedService</code>
83 * yourself in those cases.
84 */
85 public boolean isRequired() {
86 return true;
87 }
88
Marcel Offermanse14b3422009-11-25 23:04:32 +000089 public boolean isInstanceBound() {
90 // for now, configuration dependencies never are
91 return false;
92 }
93
Marcel Offermansa962bc92009-11-21 17:59:33 +000094 /**
95 * Returns <code>true</code> when configuration properties should be propagated
96 * as service properties.
97 */
98 public boolean isPropagated() {
99 return m_propagate;
100 }
101
102 public Dictionary getConfiguration() {
103 return m_settings;
104 }
105
Marcel Offermanse14b3422009-11-25 23:04:32 +0000106 public void start(DependencyService service) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000107 boolean needsStarting = false;
108 synchronized (this) {
109 m_services.add(service);
110 if (!m_isStarted) {
111 m_isStarted = true;
112 needsStarting = true;
113 }
114 }
115 if (needsStarting) {
116 Properties props = new Properties();
117 props.put(Constants.SERVICE_PID, m_pid);
118 m_registration = m_context.registerService(ManagedService.class.getName(), this, props);
119 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000120 }
121
Marcel Offermanse14b3422009-11-25 23:04:32 +0000122 public void stop(DependencyService service) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000123 boolean needsStopping = false;
124 synchronized (this) {
125 if (m_services.size() == 1 && m_services.contains(service)) {
126 m_isStarted = false;
127 needsStopping = true;
128 }
129 }
130 if (needsStopping) {
131 m_registration.unregister();
132 m_registration = null;
133 m_services.remove(service);
134 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000135 }
136
Pierre De Ropbd642a62009-12-04 22:50:31 +0000137 public ConfigurationDependency setCallback(String callback) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000138 m_callback = callback;
139 return this;
140 }
141
142 public void updated(Dictionary settings) throws ConfigurationException {
Marcel Offermanscae61362009-12-01 08:37:10 +0000143 m_updateInvokedCache.clear();
144
145 Dictionary oldSettings = null;
146 synchronized (this) {
147 oldSettings = m_settings;
148 }
149
150 if (oldSettings == null && settings == null) {
151 // CM has started but our configuration is not still present in the CM database: ignore
152 return;
153 }
154
155 Object[] services = m_services.toArray();
156 for (int i = 0; i < services.length; i++) {
157 DependencyService ds = (DependencyService) services[i];
158 // if non-null settings come in, we have to instantiate the service and
159 // apply these settings
160 ds.initService();
161 Object service = ds.getService();
162
163 if (service != null) {
164 invokeUpdate(ds, service, settings);
165 }
166 else {
167 m_logger.log(Logger.LOG_ERROR, "Service " + ds + " with configuration dependency " + this + " could not be instantiated.");
168 return;
169 }
170 }
171
Marcel Offermansa962bc92009-11-21 17:59:33 +0000172 synchronized (this) {
Marcel Offermanscae61362009-12-01 08:37:10 +0000173 m_settings = settings;
Marcel Offermansa962bc92009-11-21 17:59:33 +0000174 }
175
Marcel Offermanscae61362009-12-01 08:37:10 +0000176 for (int i = 0; i < services.length; i++) {
177 DependencyService ds = (DependencyService) services[i];
178 // If these settings did not cause a configuration exception, we determine if they have
179 // caused the dependency state to change
180 if ((oldSettings == null) && (settings != null)) {
181 ds.dependencyAvailable(this);
182 }
183 if ((oldSettings != null) && (settings == null)) {
184 ds.dependencyUnavailable(this);
185 }
186 if ((oldSettings != null) && (settings != null)) {
187 ds.dependencyChanged(this);
188 }
189 }
190 }
191
192 public void invokeUpdate(DependencyService ds, Object service, Dictionary settings) throws ConfigurationException {
193 if (m_updateInvokedCache.add(ds)) {
194 String callback = (m_callback == null) ? "updated" : m_callback;
195 Method m;
196 try {
197 m = service.getClass().getDeclaredMethod(callback, new Class[] { Dictionary.class });
198 m.setAccessible(true);
199 // if exception is thrown here, what does that mean for the
200 // state of this dependency? how smart do we want to be??
201 // it's okay like this, if the new settings contain errors, we
202 // remain in the state we were, assuming that any error causes
203 // the "old" configuration to stay in effect.
204 // CM will log any thrown exceptions.
205 m.invoke(service, new Object[] { settings });
206 }
207 catch (InvocationTargetException e) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000208 // The component has thrown an exception during it's callback invocation.
209 if (e.getTargetException() instanceof ConfigurationException) {
210 // the callback threw an OSGi ConfigurationException: just re-throw it.
211 throw (ConfigurationException) e.getTargetException();
212 }
213 else {
214 // wrap the callback exception into a ConfigurationException.
Marcel Offermanscae61362009-12-01 08:37:10 +0000215 throw new ConfigurationException(null, "Service " + ds + " with " + this.toString() + " could not be updated", e.getTargetException());
Marcel Offermansa962bc92009-11-21 17:59:33 +0000216 }
217 }
218 catch (Throwable t) {
219 // wrap any other exception as a ConfigurationException.
Marcel Offermanscae61362009-12-01 08:37:10 +0000220 throw new ConfigurationException(null, "Service " + ds + " with " + this.toString() + " could not be updated", t);
Marcel Offermansa962bc92009-11-21 17:59:33 +0000221 }
222 }
Marcel Offermanscae61362009-12-01 08:37:10 +0000223 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000224
225 /**
226 * Sets the <code>service.pid</code> of the configuration you
227 * are depending on.
228 */
229 public ConfigurationDependency setPid(String pid) {
230 ensureNotActive();
231 m_pid = pid;
232 return this;
233 }
234
235 /**
236 * Sets propagation of the configuration properties to the service
237 * properties. Any additional service properties specified directly
238 * are merged with these.
239 */
240 public ConfigurationDependency setPropagate(boolean propagate) {
241 ensureNotActive();
242 m_propagate = propagate;
243 return this;
244 }
245
246 private void ensureNotActive() {
Marcel Offermanscae61362009-12-01 08:37:10 +0000247 if (m_services != null && m_services.size() > 0) {
Marcel Offermansa962bc92009-11-21 17:59:33 +0000248 throw new IllegalStateException("Cannot modify state while active.");
249 }
250 }
251
252 public String toString() {
253 return "ConfigurationDependency[" + m_pid + "]";
254 }
255
256 public String getName() {
257 return m_pid;
258 }
259
260 public int getState() {
261 return (isAvailable() ? 1 : 0) + (isRequired() ? 2 : 0);
262 }
263
264 public String getType() {
265 return "configuration";
266 }
Marcel Offermans001db052009-12-08 08:58:40 +0000267
268 public Object getAutoConfigInstance() {
269 return getConfiguration();
270 }
271
272 public String getAutoConfigName() {
273 // TODO Auto-generated method stub
274 return null;
275 }
276
277 public Class getAutoConfigType() {
278 return Dictionary.class;
279 }
280
281 public void invokeAdded(DependencyService service) {
282 try {
283 invokeUpdate(service, service.getService(), getConfiguration());
284 }
285 catch (ConfigurationException e) {
286 // if this happens, it's definitely an inconsistency, since we
287 // asked the instance the same question before (if this is a
288 // valid configuration) and then it was
289 e.printStackTrace();
290 }
291 }
292
293 public void invokeRemoved(DependencyService service) {
294 // TODO Auto-generated method stub
295 }
296
297 public boolean isAutoConfig() {
298 // TODO Auto-generated method stub
299 return false;
300 }
Marcel Offermans117aa2f2009-12-10 09:48:17 +0000301
302 public Dictionary getProperties() {
303 return getConfiguration();
304 }
Marcel Offermansa962bc92009-11-21 17:59:33 +0000305}