blob: eccdd2c0a5ab53e0b09bad233bcb1afa2f074041 [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
Pierre De Rop3a00a212015-03-01 09:27:46 +000021import java.lang.reflect.InvocationTargetException;
22import java.util.Dictionary;
23import java.util.Enumeration;
24import java.util.Hashtable;
25
26import org.apache.felix.dm.Component;
27import org.apache.felix.dm.Dependency;
28import org.apache.felix.dm.DependencyManager;
29import org.apache.felix.dm.Logger;
30import org.apache.felix.dm.PropertyMetaData;
31import org.apache.felix.dm.context.DependencyContext;
32import org.apache.felix.dm.impl.metatype.MetaTypeProviderImpl;
33import org.osgi.framework.BundleContext;
34import org.osgi.framework.Constants;
35import org.osgi.service.cm.ManagedServiceFactory;
36import org.osgi.service.metatype.MetaTypeProvider;
37import org.osgi.service.metatype.ObjectClassDefinition;
38
39/**
40 * Factory configuration adapter service implementation. This class extends the FilterService in order to catch
41 * some Service methods for configuring actual adapter service implementation.
42 *
43 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
44 */
45public class FactoryConfigurationAdapterImpl extends FilterComponent {
46 // Our Managed Service Factory PID
47 protected final String m_factoryPid;
48
49 // Our logger
50 protected final Logger m_logger;
51
Pierre De Ropc40d93f2015-05-04 20:25:57 +000052 public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate, Object updateCallbackInstance) {
Pierre De Rop3a00a212015-03-01 09:27:46 +000053 super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
54 m_factoryPid = factoryPid;
55 m_logger = ((ComponentImpl) m_component).getLogger();
56
57 Hashtable<String, Object> props = new Hashtable<>();
58 props.put(Constants.SERVICE_PID, factoryPid);
59 m_component
60 .setInterface(ManagedServiceFactory.class.getName(), props)
Pierre De Ropc40d93f2015-05-04 20:25:57 +000061 .setImplementation(new AdapterImpl(update, propagate, updateCallbackInstance))
Pierre De Rop3a00a212015-03-01 09:27:46 +000062 .setCallbacks("init", null, "stop", null);
63 }
64
Pierre De Ropc40d93f2015-05-04 20:25:57 +000065 public FactoryConfigurationAdapterImpl(DependencyManager dm, String factoryPid, String update, boolean propagate, Object updateCallbackInstance,
Pierre De Rop3a00a212015-03-01 09:27:46 +000066 BundleContext bctx, Logger logger, String heading, String description, String localization, PropertyMetaData[] properyMetaData) {
67 super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control.
68 m_factoryPid = factoryPid;
69 m_logger = logger;
70 Hashtable<String, Object> props = new Hashtable<>();
71 props.put(Constants.SERVICE_PID, factoryPid);
72 m_component
73 .setInterface(ManagedServiceFactory.class.getName(), props)
Pierre De Ropc40d93f2015-05-04 20:25:57 +000074 .setImplementation(new MetaTypeAdapterImpl(update, propagate, updateCallbackInstance,
Pierre De Rop3a00a212015-03-01 09:27:46 +000075 bctx, logger, heading, description,
76 localization, properyMetaData))
77 .setCallbacks("init", null, "stop", null);
78 }
79
80 public String getName() {
81 return "Adapter for factory pid " + m_factoryPid;
82 }
83
84 /**
85 * Creates, updates, or removes a service, when a ConfigAdmin factory configuration is created/updated or deleted.
86 */
87 public class AdapterImpl extends AbstractDecorator implements ManagedServiceFactory {
88 // The adapter "update" method used to provide the configuration
89 protected final String m_update;
90
91 // Tells if the CM config must be propagated along with the adapter service properties
92 protected final boolean m_propagate;
Pierre De Ropc40d93f2015-05-04 20:25:57 +000093
94 // A specific callback instance where the update callback is invoked on (or null if default component instances should be used).
95 protected final Object m_updateCallbackInstance;
Pierre De Rop3a00a212015-03-01 09:27:46 +000096
97 /**
98 * Creates a new CM factory configuration adapter.
99 *
100 * @param factoryPid
101 * @param updateMethod
102 * @param adapterInterface
103 * @param adapterImplementation
104 * @param adapterProperties
105 * @param propagate
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000106 * @param updateCallbackObject null if update should be called on all component instances (composition), or a specific update callback instance.
Pierre De Rop3a00a212015-03-01 09:27:46 +0000107 */
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000108 public AdapterImpl(String updateMethod, boolean propagate, Object updateCallbackObject) {
Pierre De Rop3a00a212015-03-01 09:27:46 +0000109 m_update = updateMethod;
110 m_propagate = propagate;
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000111 m_updateCallbackInstance = updateCallbackObject;
Pierre De Rop3a00a212015-03-01 09:27:46 +0000112 }
113
114 /**
115 * Returns the managed service factory name.
116 */
117 public String getName() {
118 return m_factoryPid;
119 }
120
121 /**
122 * Method called from our superclass, when we need to create a service.
123 */
124 @SuppressWarnings("unchecked")
125 public Component createService(Object[] properties) {
126 Dictionary<String, ?> settings = (Dictionary<String, ?>) properties[0];
127 Component newService = m_manager.createComponent();
Pierre De Rop3a00a212015-03-01 09:27:46 +0000128
129 // Merge adapter service properties, with CM settings
130 Dictionary<String, Object> serviceProperties = getServiceProperties(settings);
131 newService.setInterface(m_serviceInterfaces, serviceProperties);
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000132 newService.setImplementation(m_serviceImpl);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000133 newService.setComposition(m_compositionInstance, m_compositionMethod); // if not set, no effect
134 newService.setCallbacks(m_callbackObject, m_init, m_start, m_stop, m_destroy); // if not set, no effect
135 configureAutoConfigState(newService, m_component);
136
137 for (DependencyContext dc : m_component.getDependencies()) {
138 newService.add((Dependency) dc.createCopy());
139 }
140
141 for (int i = 0; i < m_stateListeners.size(); i ++) {
142 newService.add(m_stateListeners.get(i));
143 }
144
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000145 // Instantiate the component, because we need to invoke the updated callback synchronously, in the CM calling thread.
146 ((ComponentImpl) newService).instantiateComponent();
147
148 try {
149 for (Object instance : getCompositionInstances(newService)) {
150 InvocationUtil.invokeCallbackMethod(instance, m_update,
151 new Class[][] {
152 {Dictionary.class},
153 {Component.class, Dictionary.class},
154 {}},
155 new Object[][] {
156 {settings},
157 {newService, settings},
158 {}});
159 }
160 }
161
162 catch (Throwable t) {
163 handleException(t); // will rethrow a runtime exception.
164 }
165
Pierre De Rop3a00a212015-03-01 09:27:46 +0000166 return newService;
167 }
168
169 /**
170 * Method called from our superclass, when we need to update a Service, because
171 * the configuration has changed.
172 */
173 @SuppressWarnings("unchecked")
174 public void updateService(Object[] properties) {
175 Dictionary<String, ?> cmSettings = (Dictionary<String, ?>) properties[0];
176 Component service = (Component) properties[1];
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000177 Object[] instances = getUpdateCallbackInstances(service);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000178
179 try {
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000180 for (Object instance : instances) {
181 InvocationUtil.invokeCallbackMethod(instance, m_update,
182 new Class[][] {
183 { Dictionary.class },
184 { Component.class, Dictionary.class },
185 {}},
186 new Object[][] {
187 { cmSettings },
188 { service, cmSettings },
189 {}
190 });
191 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000192 if (m_serviceInterfaces != null && m_propagate == true) {
193 Dictionary<String, ?> serviceProperties = getServiceProperties(cmSettings);
194 service.setServiceProperties(serviceProperties);
195 }
196 }
197
198 catch (Throwable t) {
199 handleException(t);
200 }
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000201 }
202
203 /**
204 * Returns the Update callback instances.
205 */
206 private Object[] getUpdateCallbackInstances(Component comp) {
207 if (m_updateCallbackInstance == null) {
208 return comp.getInstances();
209 } else {
210 return new Object[] { m_updateCallbackInstance };
211 }
212 }
213
214 private Object[] getCompositionInstances(Component component) {
215 if (m_updateCallbackInstance != null) {
216 return new Object[] { m_updateCallbackInstance };
217 } else {
218 return component.getInstances();
219 }
220 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000221
222 /**
223 * Merge CM factory configuration setting with the adapter service properties. The private CM factory configuration
224 * settings are ignored. A CM factory configuration property is private if its name starts with a dot (".").
225 *
226 * @param adapterProperties
227 * @param settings
228 * @return
229 */
230 private Dictionary<String, Object> getServiceProperties(Dictionary<String, ?> settings) {
231 Dictionary<String, Object> props = new Hashtable<>();
232
233 // Add adapter Service Properties
234 if (m_serviceProperties != null) {
235 Enumeration<String> keys = m_serviceProperties.keys();
236 while (keys.hasMoreElements()) {
237 String key = keys.nextElement();
238 Object val = m_serviceProperties.get(key);
239 props.put(key, val);
240 }
241 }
242
243 if (m_propagate) {
244 // Add CM setting into adapter service properties.
245 // (CM setting will override existing adapter service properties).
246 Enumeration<String> keys = settings.keys();
247 while (keys.hasMoreElements()) {
248 String key = keys.nextElement();
249 if (! key.toString().startsWith(".")) {
250 // public properties are propagated
251 Object val = settings.get(key);
252 props.put(key, val);
253 }
254 }
255 }
256
257
258 return props;
259 }
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000260
Pierre De Rop3a00a212015-03-01 09:27:46 +0000261 private void handleException(Throwable t) {
262 m_logger.log(Logger.LOG_ERROR, "Got exception while handling configuration update for factory pid " + m_factoryPid, t);
263 if (t instanceof InvocationTargetException) {
264 // Our super class will check if the target exception is itself a ConfigurationException.
265 // In this case, it will simply re-thrown.
266 throw new RuntimeException(((InvocationTargetException) t).getTargetException());
267 }
268 else if (t instanceof RuntimeException) {
269 throw (RuntimeException) t;
270 }
271 else {
272 throw new RuntimeException(t);
273 }
274 }
275 }
276
277
278 /**
279 * Extends AdapterImpl for MetaType support.
280 */
281 class MetaTypeAdapterImpl extends AdapterImpl implements MetaTypeProvider {
282 // Our MetaType Provider for describing our properties metadata
283 private final MetaTypeProviderImpl m_metaType;
284
285 public MetaTypeAdapterImpl(String updateMethod, boolean propagate,
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000286 Object updateCallbackInstance,
Pierre De Rop3a00a212015-03-01 09:27:46 +0000287 BundleContext bctx, Logger logger, String heading,
288 String description, String localization,
289 PropertyMetaData[] properyMetaData) {
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000290 super(updateMethod, propagate, updateCallbackInstance);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000291 m_metaType = new MetaTypeProviderImpl(m_factoryPid, bctx, logger, null, this);
292 m_metaType.setName(heading);
293 m_metaType.setDescription(description);
294 if (localization != null) {
295 m_metaType.setLocalization(localization);
296 }
297 for (int i = 0; i < properyMetaData.length; i++) {
298 m_metaType.add(properyMetaData[i]);
299 }
300 }
301
302 public String[] getLocales() {
303 return m_metaType.getLocales();
304 }
305
306 public ObjectClassDefinition getObjectClassDefinition(String id, String locale) {
307 return m_metaType.getObjectClassDefinition(id, locale);
308 }
309 }
310}