blob: 4016a2c5882fb1e0c7a697c15817fcf991f0e326 [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 static org.apache.felix.dm.ComponentState.INACTIVE;
22import static org.apache.felix.dm.ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED;
23import static org.apache.felix.dm.ComponentState.TRACKING_OPTIONAL;
24import static org.apache.felix.dm.ComponentState.WAITING_FOR_REQUIRED;
25
26import java.lang.reflect.Constructor;
27import java.lang.reflect.InvocationTargetException;
28import java.lang.reflect.Method;
29import java.lang.reflect.Proxy;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.Dictionary;
33import java.util.Enumeration;
34import java.util.HashMap;
35import java.util.Hashtable;
Pierre De Ropc8295c22015-06-04 10:15:35 +000036import java.util.IdentityHashMap;
Pierre De Rop3a00a212015-03-01 09:27:46 +000037import java.util.List;
38import java.util.Map;
39import java.util.Set;
40import java.util.concurrent.ConcurrentHashMap;
41import java.util.concurrent.ConcurrentSkipListSet;
42import java.util.concurrent.CopyOnWriteArrayList;
43import java.util.concurrent.Executor;
44import java.util.concurrent.atomic.AtomicBoolean;
45import java.util.concurrent.atomic.AtomicLong;
46
47import org.apache.felix.dm.Component;
48import org.apache.felix.dm.ComponentDeclaration;
49import org.apache.felix.dm.ComponentDependencyDeclaration;
Pierre De Ropc8295c22015-06-04 10:15:35 +000050import org.apache.felix.dm.ComponentExecutorFactory;
Pierre De Rop3a00a212015-03-01 09:27:46 +000051import org.apache.felix.dm.ComponentState;
52import org.apache.felix.dm.ComponentStateListener;
53import org.apache.felix.dm.Dependency;
54import org.apache.felix.dm.DependencyManager;
55import org.apache.felix.dm.Logger;
56import org.apache.felix.dm.context.ComponentContext;
57import org.apache.felix.dm.context.DependencyContext;
Pierre De Rop3a00a212015-03-01 09:27:46 +000058import org.apache.felix.dm.context.Event;
Pierre De Ropc8295c22015-06-04 10:15:35 +000059import org.apache.felix.dm.context.EventType;
Pierre De Rop3a00a212015-03-01 09:27:46 +000060import org.osgi.framework.Bundle;
61import org.osgi.framework.BundleContext;
62import org.osgi.framework.ServiceRegistration;
63import org.osgi.service.log.LogService;
64
65/**
66 * Dependency Manager Component implementation.
67 *
68 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
69 */
70public class ComponentImpl implements Component, ComponentContext, ComponentDeclaration {
Pierre De Ropc8295c22015-06-04 10:15:35 +000071 /**
72 * NullObject ServiceRegistration that is injected in components that don't provide any services.
73 */
Pierre De Rop3a00a212015-03-01 09:27:46 +000074 private static final ServiceRegistration NULL_REGISTRATION = (ServiceRegistration) Proxy
75 .newProxyInstance(ComponentImpl.class.getClassLoader(),
76 new Class[] { ServiceRegistration.class },
77 new DefaultNullObject());
Pierre De Ropc8295c22015-06-04 10:15:35 +000078
79 /**
80 * Constant Used to get empty constructor by reflection.
81 */
Pierre De Rop3a00a212015-03-01 09:27:46 +000082 private static final Class<?>[] VOID = new Class[] {};
Pierre De Ropc8295c22015-06-04 10:15:35 +000083
84 /**
85 * Default Component Executor, which is by default single threaded. The first thread which schedules a task
86 * is the master thread and will execute all tasks that are scheduled by other threads at the time the master
87 * thread is executing. Internal tasks scheduled by the master thread are executed immediately (inline execution).
88 *
89 * If a ComponentExecutorFactory is provided in the OSGI registry, then this executor will be replaced by the
90 * executor returned by the ComponentExecutorFactory (however, the same semantic of the default executor is used:
91 * all tasks are serially executed).
92 *
93 * @see @link {@link ComponentExecutorFactory}
94 */
Pierre De Rop3a00a212015-03-01 09:27:46 +000095 private volatile Executor m_executor = new SerialExecutor(new Logger(null));
Pierre De Ropc8295c22015-06-04 10:15:35 +000096
97 /**
98 * The current state of the component state machine.
99 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000100 private ComponentState m_state = ComponentState.INACTIVE;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000101
102 /**
103 * Indicates that the handleChange method is currently being executed.
104 */
105 private boolean m_handlingChange;
106
107 /**
108 * List of dependencies. We use a COW list in order to avoid ConcurrentModificationException while iterating on the
109 * list and while a component synchronously add more dependencies from one of its callback method.
110 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000111 private final CopyOnWriteArrayList<DependencyContext> m_dependencies = new CopyOnWriteArrayList<>();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000112
113 /**
114 * List of Component state listeners. We use a COW list in order to avoid ConcurrentModificationException while iterating on the
115 * list and while a component synchronously add more listeners from one of its callback method.
116 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000117 private final List<ComponentStateListener> m_listeners = new CopyOnWriteArrayList<>();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000118
119 /**
120 * Is the component active ?
121 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000122 private boolean m_isStarted;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000123
124 /**
125 * The Component logger.
126 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000127 private final Logger m_logger;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000128
129 /**
130 * The Component bundle context.
131 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000132 private final BundleContext m_context;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000133
134 /**
135 * The DependencyManager object that has created this component.
136 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000137 private final DependencyManager m_manager;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000138
139 /**
140 * The object used to create the component. Can be a class name, or the component implementation instance.
141 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000142 private Object m_componentDefinition;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000143
144 /**
145 * The component instance.
146 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000147 private Object m_componentInstance;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000148
149 /**
150 * The service(s) provided by this component. Can be a String, or a String array.
151 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000152 private volatile Object m_serviceName;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000153
154 /**
155 * The service properties, if this component is providing a service.
156 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000157 private volatile Dictionary<Object, Object> m_serviceProperties;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000158
159 /**
160 * The component service registration. Can be a NullObject in case the component does not provide a service.
161 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000162 private volatile ServiceRegistration m_registration;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000163
164 /**
165 * Map of auto configured fields (BundleContext, ServiceRegistration, DependencyManager, or Component).
166 * By default, all fields mentioned above are auto configured (injected in class fields having the same type).
167 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000168 private final Map<Class<?>, Boolean> m_autoConfig = new ConcurrentHashMap<>();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000169
170 /**
171 * Map of auto configured instance fields that will be used when injected auto configured fields.
172 * @see #m_autoConfig
173 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000174 private final Map<Class<?>, String> m_autoConfigInstance = new ConcurrentHashMap<>();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000175
176 /**
177 * Data structure used to record the elapsed time used by component lifecycle callbacks.
178 * Key = callback name ("init", "start", "stop", "destroy").
179 * Value = elapsed time in nanos.
180 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000181 private final Map<String, Long> m_stopwatch = new ConcurrentHashMap<>();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000182
183 /**
184 * Unique component id.
185 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000186 private final long m_id;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000187
188 /**
189 * Unique ID generator.
190 */
191 private final static AtomicLong m_idGenerator = new AtomicLong();
192
193 /**
194 * Holds all the services of a given dependency context. Caution: the last entry in the skiplist is the highest
195 * ranked service.
196 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000197 private final Map<DependencyContext, ConcurrentSkipListSet<Event>> m_dependencyEvents = new HashMap<>();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000198
199 /**
200 * Flag used to check if this component has been added in a DependencyManager object.
201 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000202 private final AtomicBoolean m_active = new AtomicBoolean(false);
203
Pierre De Ropc8295c22015-06-04 10:15:35 +0000204 /**
205 * Init lifecycle callback. From that method, component are expected to add more extra dependencies.
206 * When this callback is invoked, all required dependencies have been injected.
207 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000208 private volatile String m_callbackInit;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000209
210 /**
211 * Start lifecycle callback. When this method is called, all required + all extra required dependencies defined in the
212 * init callback have been injected. The component may then perform its initialization.
213 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000214 private volatile String m_callbackStart;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000215
216 /**
217 * Stop callback. When this method is called, the component has been unregistered (if it provides any services),
218 * and all optional dependencies have been unbound.
219 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000220 private volatile String m_callbackStop;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000221
222 /**
223 * Destroy callback. When this method is called, all required dependencies defined in the init method have been unbound.
224 * After this method is called, then all required dependencies defined in the Activator will be unbound.
225 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000226 private volatile String m_callbackDestroy;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000227
228 /**
229 * By default, the init/start/stop/destroy callbacks are invoked on the component instance(s).
230 * But you can specify a separate callback instance.
231 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000232 private volatile Object m_callbackInstance;
233
Pierre De Ropc8295c22015-06-04 10:15:35 +0000234 /**
235 * Component Factory instance object, that can be used to instantiate the component instance.
236 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000237 private volatile Object m_instanceFactory;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000238
239 /**
240 * Name of the Factory method to call.
241 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000242 private volatile String m_instanceFactoryCreateMethod;
243
Pierre De Ropc8295c22015-06-04 10:15:35 +0000244 /**
245 * Composition Manager that can be used to create a graph of objects that are used to implement the component.
246 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000247 private volatile Object m_compositionManager;
Pierre De Rop3a00a212015-03-01 09:27:46 +0000248
Pierre De Ropc8295c22015-06-04 10:15:35 +0000249 /**
250 * Name of the method used to invoke in order to get the list of component instance objects.
251 */
252 private volatile String m_compositionManagerGetMethod;
253
254 /**
255 * The composition manager instance object, if specified.
256 */
257 private volatile Object m_compositionManagerInstance;
258
259 /**
260 * The Component bundle.
261 */
262 private final Bundle m_bundle;
263
264 /**
265 * Cache of callback invocation used to avoid calling the same callback twice.
266 * This situation may sometimes happen when the state machine triggers a lifecycle callback ("bind" call), and
267 * when the bind method registers a service which is tracked by another optional component dependency.
268 *
269 * @see org.apache.felix.dm.itest.api.FELIX4913_OptionalCallbackInvokedTwiceTest which reproduces the use case.
270 */
271 private final Map<Event, Event> m_invokeCallbackCache = new IdentityHashMap<>();
Pierre De Rop1eb4a452015-09-18 22:09:45 +0000272
273 /**
274 * Flag used to check if the start callback has been invoked.
275 * We use this flag to ensure that we only inject optional dependencies after the start callback has been called.
276 */
277 private boolean m_startCalled;
Pierre De Ropc8295c22015-06-04 10:15:35 +0000278
279 /**
280 * Default component declaration implementation.
281 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000282 static class SCDImpl implements ComponentDependencyDeclaration {
283 private final String m_name;
284 private final int m_state;
285 private final String m_type;
286
287 public SCDImpl(String name, int state, String type) {
288 m_name = name;
289 m_state = state;
290 m_type = type;
291 }
292
293 public String getName() {
294 return m_name;
295 }
296
297 public String getSimpleName() {
298 return m_name;
299 }
300
301 public String getFilter() {
302 return null;
303 }
304
305 public int getState() {
306 return m_state;
307 }
308
309 public String getType() {
310 return m_type;
311 }
312 }
313
Pierre De Ropc8295c22015-06-04 10:15:35 +0000314 /**
315 * Constructor. Only used for tests.
316 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000317 public ComponentImpl() {
318 this(null, null, new Logger(null));
319 }
320
Pierre De Ropc8295c22015-06-04 10:15:35 +0000321 /**
322 * Constructor
323 * @param context the component bundle context
324 * @param manager the manager used to create the component
325 * @param logger the logger to use
326 */
Pierre De Rop3a00a212015-03-01 09:27:46 +0000327 public ComponentImpl(BundleContext context, DependencyManager manager, Logger logger) {
328 m_context = context;
329 m_bundle = context != null ? context.getBundle() : null;
330 m_manager = manager;
331 m_logger = logger;
332 m_autoConfig.put(BundleContext.class, Boolean.TRUE);
333 m_autoConfig.put(ServiceRegistration.class, Boolean.TRUE);
334 m_autoConfig.put(DependencyManager.class, Boolean.TRUE);
335 m_autoConfig.put(Component.class, Boolean.TRUE);
336 m_callbackInit = "init";
337 m_callbackStart = "start";
338 m_callbackStop = "stop";
339 m_callbackDestroy = "destroy";
340 m_id = m_idGenerator.getAndIncrement();
341 }
342
Pierre De Ropc8295c22015-06-04 10:15:35 +0000343 @Override
344 public Component setDebug(String debugKey) {
345 // Force debug level in our logger
346 m_logger.setEnabledLevel(LogService.LOG_DEBUG);
347 m_logger.setDebugKey(debugKey);
348 return this;
349 }
350
Pierre De Rop3a00a212015-03-01 09:27:46 +0000351 @Override
352 public Component add(final Dependency ... dependencies) {
353 getExecutor().execute(new Runnable() {
354 @Override
355 public void run() {
356 List<DependencyContext> instanceBoundDeps = new ArrayList<>();
357 for (Dependency d : dependencies) {
358 DependencyContext dc = (DependencyContext) d;
359 if (dc.getComponentContext() != null) {
360 m_logger.err("%s can't be added to %s (dependency already added to another component).", dc,
361 ComponentImpl.this);
362 continue;
363 }
364 m_dependencyEvents.put(dc, new ConcurrentSkipListSet<Event>());
365 m_dependencies.add(dc);
366 dc.setComponentContext(ComponentImpl.this);
367 if (!(m_state == ComponentState.INACTIVE)) {
368 dc.setInstanceBound(true);
369 instanceBoundDeps.add(dc);
370 }
371 }
372 startDependencies(instanceBoundDeps);
373 handleChange();
374 }
375 });
376 return this;
377 }
378
379 @Override
380 public Component remove(final Dependency d) {
381 getExecutor().execute(new Runnable() {
382 @Override
383 public void run() {
384 DependencyContext dc = (DependencyContext) d;
385 // First remove this dependency from the dependency list
386 m_dependencies.remove(d);
387 // Now we can stop the dependency (our component won't be deactivated, it will only be unbound with
388 // the removed dependency).
389 if (!(m_state == ComponentState.INACTIVE)) {
390 dc.stop();
391 }
392 // Finally, cleanup the dependency events.
393 m_dependencyEvents.remove(d);
394 handleChange();
395 }
396 });
397 return this;
398 }
399
Pierre De Ropc8295c22015-06-04 10:15:35 +0000400 @Override
Pierre De Rop3a00a212015-03-01 09:27:46 +0000401 public void start() {
402 if (m_active.compareAndSet(false, true)) {
403 getExecutor().execute(new Runnable() {
404 @Override
405 public void run() {
406 m_isStarted = true;
407 handleChange();
408 }
409 });
410 }
411 }
412
Pierre De Ropc8295c22015-06-04 10:15:35 +0000413 @Override
Pierre De Rop3a00a212015-03-01 09:27:46 +0000414 public void stop() {
415 if (m_active.compareAndSet(true, false)) {
416 Executor executor = getExecutor();
417
418 // First, declare the task that will stop our component in our executor.
419 final Runnable stopTask = new Runnable() {
420 @Override
421 public void run() {
422 m_isStarted = false;
423 handleChange();
424 }
425 };
426
427 // Now, we have to schedule our stopTask in our component executor. But we have to handle a special case:
428 // if the component bundle is stopping *AND* if the executor is a parallel dispatcher, then we want
429 // to invoke our stopTask synchronously, in order to make sure that the bundle context is valid while our
430 // component is being deactivated (if we stop the component asynchronously, the bundle context may be invalidated
431 // before our component is stopped, and we don't want to be in this situation).
432
433 boolean stopping = m_bundle != null /* null only in tests env */ && m_bundle.getState() == Bundle.STOPPING;
434 if (stopping && executor instanceof DispatchExecutor) {
435 ((DispatchExecutor) executor).execute(stopTask, false /* try to execute synchronously, not using threadpool */);
436 } else {
437 executor.execute(stopTask);
438 }
439 }
440 }
441
442 @SuppressWarnings("unchecked")
443 @Override
444 public Component setInterface(String serviceName, Dictionary<?, ?> properties) {
445 ensureNotActive();
446 m_serviceName = serviceName;
447 m_serviceProperties = (Dictionary<Object, Object>) properties;
448 return this;
449 }
450
451 @SuppressWarnings("unchecked")
452 @Override
453 public Component setInterface(String[] serviceName, Dictionary<?, ?> properties) {
454 ensureNotActive();
455 m_serviceName = serviceName;
456 m_serviceProperties = (Dictionary<Object, Object>) properties;
457 return this;
458 }
459
460 @Override
461 public void handleEvent(final DependencyContext dc, final EventType type, final Event... event) {
462 // since this method can be invoked by anyone from any thread, we need to
463 // pass on the event to a runnable that we execute using the component's
464 // executor
465 getExecutor().execute(new Runnable() {
466 @Override
467 public void run() {
Pierre De Ropc8295c22015-06-04 10:15:35 +0000468 try {
469 switch (type) {
470 case ADDED:
471 handleAdded(dc, event[0]);
472 break;
473 case CHANGED:
474 handleChanged(dc, event[0]);
475 break;
476 case REMOVED:
477 handleRemoved(dc, event[0]);
478 break;
479 case SWAPPED:
480 handleSwapped(dc, event[0], event[1]);
481 break;
482 }
483 } finally {
484 // Clear cache of component callbacks invocation, except if we are currently called from handleChange().
485 // (See FELIX-4913).
486 clearInvokeCallbackCache();
Pierre De Rop3a00a212015-03-01 09:27:46 +0000487 }
488 }
489 });
490 }
491
492 @Override
493 public Event getDependencyEvent(DependencyContext dc) {
494 ConcurrentSkipListSet<Event> events = m_dependencyEvents.get(dc);
495 return events.size() > 0 ? events.last() : null;
496 }
497
498 @Override
499 public Set<Event> getDependencyEvents(DependencyContext dc) {
500 return m_dependencyEvents.get(dc);
501 }
502
Pierre De Ropc8295c22015-06-04 10:15:35 +0000503 @Override
Pierre De Rop3a00a212015-03-01 09:27:46 +0000504 public Component setAutoConfig(Class<?> clazz, boolean autoConfig) {
505 m_autoConfig.put(clazz, Boolean.valueOf(autoConfig));
506 return this;
507 }
508
Pierre De Ropc8295c22015-06-04 10:15:35 +0000509 @Override
Pierre De Rop3a00a212015-03-01 09:27:46 +0000510 public Component setAutoConfig(Class<?> clazz, String instanceName) {
511 m_autoConfig.put(clazz, Boolean.valueOf(instanceName != null));
512 m_autoConfigInstance.put(clazz, instanceName);
513 return this;
514 }
515
Pierre De Ropc8295c22015-06-04 10:15:35 +0000516 @Override
Pierre De Rop3a00a212015-03-01 09:27:46 +0000517 public boolean getAutoConfig(Class<?> clazz) {
518 Boolean result = (Boolean) m_autoConfig.get(clazz);
519 return (result != null && result.booleanValue());
520 }
521
Pierre De Ropc8295c22015-06-04 10:15:35 +0000522 @Override
Pierre De Rop3a00a212015-03-01 09:27:46 +0000523 public String getAutoConfigInstance(Class<?> clazz) {
524 return (String) m_autoConfigInstance.get(clazz);
525 }
526
Pierre De Ropc8295c22015-06-04 10:15:35 +0000527 @SuppressWarnings("unchecked")
528 public <T> T getInstance() {
529 Object[] instances = getCompositionInstances();
530 return instances.length == 0 ? null : (T) instances[0];
531 }
532
533 public Object[] getInstances() {
534 return getCompositionInstances();
535 }
536
537 public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters) {
538 invokeCallbackMethod(instances, methodName, signatures, parameters, true);
539 }
540
541 public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures,
542 Object[][] parameters, boolean logIfNotFound) {
543 boolean callbackFound = false;
544 for (int i = 0; i < instances.length; i++) {
545 try {
546 InvocationUtil.invokeCallbackMethod(instances[i], methodName, signatures, parameters);
547 callbackFound |= true;
548 }
549 catch (NoSuchMethodException e) {
550 // if the method does not exist, ignore it
551 }
552 catch (InvocationTargetException e) {
553 // the method itself threw an exception, log that
554 m_logger.log(Logger.LOG_ERROR, "Invocation of '" + methodName + "' failed.", e.getCause());
555 }
556 catch (Throwable e) {
557 m_logger.log(Logger.LOG_ERROR, "Could not invoke '" + methodName + "'.", e);
558 }
559 }
560
561 // If the callback is not found, we don't log if the method is on an AbstractDecorator.
562 // (Aspect or Adapter are not interested in user dependency callbacks)
563 if (logIfNotFound && ! callbackFound && ! (getInstance() instanceof AbstractDecorator)) {
564 if (m_logger == null) {
565 System.out.println("Callback \"" + methodName + "\" not found on componnent instances "
566 + Arrays.toString(getInstances()));
567 } else {
568 m_logger.log(LogService.LOG_ERROR, "Callback \"" + methodName + "\" callback not found on componnent instances "
569 + Arrays.toString(getInstances()));
570 }
571
572 }
573 }
574
575 @Override
576 public boolean isAvailable() {
577 return m_state == TRACKING_OPTIONAL;
578 }
579
580 @Override
581 public boolean isActive() {
582 return m_active.get();
583 }
584
585 @Override
586 public Component add(final ComponentStateListener l) {
587 m_listeners.add(l);
588 return this;
589 }
590
591 @Override
592 public Component remove(ComponentStateListener l) {
593 m_listeners.remove(l);
594 return this;
595 }
596
597 @SuppressWarnings("unchecked")
598 @Override
599 public List<DependencyContext> getDependencies() {
600 return (List<DependencyContext>) m_dependencies.clone();
601 }
602
603 @Override
604 public Component setImplementation(Object implementation) {
605 m_componentDefinition = implementation;
606 return this;
607 }
608
609 @Override
610 public ServiceRegistration getServiceRegistration() {
611 return m_registration;
612 }
613
614 @SuppressWarnings("unchecked")
615 @Override
616 public <K,V> Dictionary<K, V> getServiceProperties() {
617 if (m_serviceProperties != null) {
618 // Applied patch from FELIX-4304
619 Hashtable<Object, Object> serviceProperties = new Hashtable<>();
620 addTo(serviceProperties, m_serviceProperties);
621 return (Dictionary<K, V>) serviceProperties;
622 }
623 return null;
624 }
625
626 @Override
627 @SuppressWarnings("unchecked")
628 public Component setServiceProperties(final Dictionary<?, ?> serviceProperties) {
629 getExecutor().execute(new Runnable() {
630 @Override
631 public void run() {
632 Dictionary<Object, Object> properties = null;
633 m_serviceProperties = (Dictionary<Object, Object>) serviceProperties;
634 if ((m_registration != null) && (m_serviceName != null)) {
635 properties = calculateServiceProperties();
636 m_registration.setProperties(properties);
637 }
638 }
639 });
640 return this;
641 }
642
643 public Component setCallbacks(String init, String start, String stop, String destroy) {
644 ensureNotActive();
645 m_callbackInit = init;
646 m_callbackStart = start;
647 m_callbackStop = stop;
648 m_callbackDestroy = destroy;
649 return this;
650 }
651
652 public Component setCallbacks(Object instance, String init, String start, String stop, String destroy) {
653 ensureNotActive();
654 m_callbackInstance = instance;
655 m_callbackInit = init;
656 m_callbackStart = start;
657 m_callbackStop = stop;
658 m_callbackDestroy = destroy;
659 return this;
660 }
661
662 @Override
663 public Component setFactory(Object factory, String createMethod) {
664 ensureNotActive();
665 m_instanceFactory = factory;
666 m_instanceFactoryCreateMethod = createMethod;
667 return this;
668 }
669
670 @Override
671 public Component setFactory(String createMethod) {
672 return setFactory(null, createMethod);
673 }
674
675 @Override
676 public Component setComposition(Object instance, String getMethod) {
677 ensureNotActive();
678 m_compositionManager = instance;
679 m_compositionManagerGetMethod = getMethod;
680 return this;
681 }
682
683 @Override
684 public Component setComposition(String getMethod) {
685 return setComposition(null, getMethod);
686 }
687
688 @Override
689 public DependencyManager getDependencyManager() {
690 return m_manager;
691 }
692
693 public ComponentDependencyDeclaration[] getComponentDependencies() {
694 List<DependencyContext> deps = getDependencies();
695 if (deps != null) {
696 ComponentDependencyDeclaration[] result = new ComponentDependencyDeclaration[deps.size()];
697 for (int i = 0; i < result.length; i++) {
698 DependencyContext dep = (DependencyContext) deps.get(i);
699 if (dep instanceof ComponentDependencyDeclaration) {
700 result[i] = (ComponentDependencyDeclaration) dep;
701 }
702 else {
703 result[i] = new SCDImpl(dep.toString(), (dep.isAvailable() ? 1 : 0) + (dep.isRequired() ? 2 : 0), dep.getClass().getName());
704 }
705 }
706 return result;
707 }
708 return null;
709 }
710
711 public String getName() {
712 StringBuffer sb = new StringBuffer();
713 Object serviceName = m_serviceName;
714 if (serviceName instanceof String[]) {
715 String[] names = (String[]) serviceName;
716 for (int i = 0; i < names.length; i++) {
717 if (i > 0) {
718 sb.append(", ");
719 }
720 sb.append(names[i]);
721 }
722 appendProperties(sb);
723 } else if (serviceName instanceof String) {
724 sb.append(serviceName.toString());
725 appendProperties(sb);
726 } else {
727 Object implementation = m_componentDefinition;
728 if (implementation != null) {
729 if (implementation instanceof Class) {
730 sb.append(((Class<?>) implementation).getName());
731 } else {
732 // If the implementation instance does not override "toString", just display
733 // the class name, else display the component using its toString method
734 try {
735 Method m = implementation.getClass().getMethod("toString", new Class[0]);
736 if (m.getDeclaringClass().equals(Object.class)) {
737 sb.append(implementation.getClass().getName());
738 } else {
739 sb.append(implementation.toString());
740 }
741 } catch (java.lang.NoSuchMethodException e) {
742 // Just display the class name
743 sb.append(implementation.getClass().getName());
744 }
745 }
746 } else {
747 sb.append(super.toString());
748 }
749 }
750 return sb.toString();
751 }
752
753 @Override
754 public BundleContext getBundleContext() {
755 return m_context;
756 }
757
758 @Override
759 public Bundle getBundle() {
760 return m_bundle;
761 }
762
763 public long getId() {
764 return m_id;
765 }
766
767 public String getClassName() {
768 Object serviceInstance = m_componentInstance;
769 if (serviceInstance != null) {
770 return serviceInstance.getClass().getName();
771 }
772
773 Object implementation = m_componentDefinition;
774 if (implementation != null) {
775 if (implementation instanceof Class) {
776 return ((Class<?>) implementation).getName();
777 }
778 return implementation.getClass().getName();
779 }
780
781 Object instanceFactory = m_instanceFactory;
782 if (instanceFactory != null) {
783 return instanceFactory.getClass().getName();
784 } else {
785 // unexpected.
786 return ComponentImpl.class.getName();
787 }
788 }
789
790 public String[] getServices() {
791 if (m_serviceName instanceof String[]) {
792 return (String[]) m_serviceName;
793 } else if (m_serviceName instanceof String) {
794 return new String[] { (String) m_serviceName };
795 } else {
796 return null;
797 }
798 }
799
800 public int getState() {
801 return (isAvailable() ? ComponentDeclaration.STATE_REGISTERED : ComponentDeclaration.STATE_UNREGISTERED);
802 }
803
804 public void ensureNotActive() {
805 if (m_active.get()) {
806 throw new IllegalStateException("Can't modify an already started component.");
807 }
808 }
809
810 public ComponentDeclaration getComponentDeclaration() {
811 return this;
812 }
813
814 @Override
815 public String toString() {
816 if (m_logger.getDebugKey() != null) {
817 return m_logger.getDebugKey();
818 }
819 return getClassName();
820 }
821
822 @Override
823 public void setThreadPool(Executor threadPool) {
824 ensureNotActive();
825 m_executor = new DispatchExecutor(threadPool, m_logger);
826 }
827
828 @Override
829 public Logger getLogger() {
830 return m_logger;
831 }
832
833 @Override
834 public Map<String, Long> getCallbacksTime() {
835 return m_stopwatch;
836 }
837
838 // ---------------------- Package/Private methods ---------------------------
839
840 void instantiateComponent() {
841 m_logger.debug("instantiating component.");
842
843 // TODO add more complex factory instantiations of one or more components in a composition here
844 if (m_componentInstance == null) {
845 if (m_componentDefinition instanceof Class) {
846 try {
847 m_componentInstance = createInstance((Class<?>) m_componentDefinition);
848 }
849 catch (Exception e) {
850 m_logger.log(Logger.LOG_ERROR, "Could not instantiate class " + m_componentDefinition, e);
851 }
852 }
853 else {
854 if (m_instanceFactoryCreateMethod != null) {
855 Object factory = null;
856 if (m_instanceFactory != null) {
857 if (m_instanceFactory instanceof Class) {
858 try {
859 factory = createInstance((Class<?>) m_instanceFactory);
860 }
861 catch (Exception e) {
862 m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
863 }
864 }
865 else {
866 factory = m_instanceFactory;
867 }
868 }
869 else {
870 // TODO review if we want to try to default to something if not specified
871 // for now the JavaDoc of setFactory(method) reflects the fact that we need
872 // to review it
873 }
874 if (factory == null) {
875 m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
876 }
877 else {
878 try {
879 m_componentInstance = InvocationUtil.invokeMethod(factory,
880 factory.getClass(), m_instanceFactoryCreateMethod,
881 new Class[][] {{}, {Component.class}}, new Object[][] {{}, {this}}, false);
882 }
883 catch (Exception e) {
884 m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
885 }
886 }
887 }
888 }
889
890 if (m_componentInstance == null) {
891 m_componentInstance = m_componentDefinition;
892 }
893
894 // configure the bundle context
895 autoConfigureImplementation(BundleContext.class, m_context);
896 autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
897 autoConfigureImplementation(DependencyManager.class, m_manager);
898 autoConfigureImplementation(Component.class, this);
899 }
900 }
901
902 /**
903 * Runs the state machine, to see if a change event has to trigger some component state transition.
904 */
905 private void handleChange() {
906 m_logger.debug("handleChanged");
907 handlingChange(true);
908 try {
909 ComponentState oldState;
910 ComponentState newState;
911 do {
912 oldState = m_state;
913 newState = calculateNewState(oldState);
914 m_logger.debug("%s -> %s", oldState, newState);
915 m_state = newState;
916 } while (performTransition(oldState, newState));
917 } finally {
918 handlingChange(false);
919 clearInvokeCallbackCache();
920 m_logger.debug("end handling change.");
921 }
922 }
923
924 /**
925 * Based on the current state, calculate the new state.
926 */
927 private ComponentState calculateNewState(ComponentState currentState) {
928 if (currentState == INACTIVE) {
929 if (m_isStarted) {
930 return WAITING_FOR_REQUIRED;
931 }
932 }
933 if (currentState == WAITING_FOR_REQUIRED) {
934 if (!m_isStarted) {
935 return INACTIVE;
936 }
937 if (allRequiredAvailable()) {
938 return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
939 }
940 }
941 if (currentState == INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
942 if (m_isStarted && allRequiredAvailable()) {
943 if (allInstanceBoundAvailable()) {
944 return TRACKING_OPTIONAL;
945 }
946 return currentState;
947 }
948 return WAITING_FOR_REQUIRED;
949 }
950 if (currentState == TRACKING_OPTIONAL) {
951 if (m_isStarted && allRequiredAvailable() && allInstanceBoundAvailable()) {
952 return currentState;
953 }
954 return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
955 }
956 return currentState;
957 }
958
959 /**
960 * Perform all the actions associated with state transitions.
961 * @returns true if a transition was performed.
962 **/
963 private boolean performTransition(ComponentState oldState, ComponentState newState) {
964 if (oldState == ComponentState.INACTIVE && newState == ComponentState.WAITING_FOR_REQUIRED) {
965 startDependencies(m_dependencies);
966 notifyListeners(newState);
967 return true;
968 }
969 if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
970 instantiateComponent();
971 invokeAutoConfigDependencies();
972 invokeAddRequiredDependencies();
973 ComponentState stateBeforeCallingInit = m_state;
974 invoke(m_callbackInit);
975 if (stateBeforeCallingInit == m_state) {
976 notifyListeners(newState); // init did not change current state, we can notify about this new state
977 }
978 return true;
979 }
980 if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.TRACKING_OPTIONAL) {
981 invokeAutoConfigInstanceBoundDependencies();
982 invokeAddRequiredInstanceBoundDependencies();
Pierre De Rop1eb4a452015-09-18 22:09:45 +0000983 invokeStart();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000984 invokeAddOptionalDependencies();
985 registerService();
986 notifyListeners(newState);
987 return true;
988 }
989 if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
990 unregisterService();
991 invokeRemoveOptionalDependencies();
Pierre De Rop1eb4a452015-09-18 22:09:45 +0000992 invokeStop();
Pierre De Ropc8295c22015-06-04 10:15:35 +0000993 invokeRemoveInstanceBoundDependencies();
994 notifyListeners(newState);
995 return true;
996 }
997 if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.WAITING_FOR_REQUIRED) {
998 invoke(m_callbackDestroy);
Pierre De Rop1088d6d2015-09-24 20:58:58 +0000999 removeInstanceBoundDependencies();
Pierre De Ropc8295c22015-06-04 10:15:35 +00001000 invokeRemoveRequiredDependencies();
1001 notifyListeners(newState);
1002 if (! someDependenciesNeedInstance()) {
1003 destroyComponent();
1004 }
1005 return true;
1006 }
1007 if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INACTIVE) {
1008 stopDependencies();
1009 destroyComponent();
1010 notifyListeners(newState);
1011 return true;
1012 }
1013 return false;
1014 }
1015
Pierre De Rop1eb4a452015-09-18 22:09:45 +00001016 private void invokeStart() {
1017 invoke(m_callbackStart);
1018 m_startCalled = true;
1019 }
1020
1021 private void invokeStop() {
1022 invoke(m_callbackStop);
1023 m_startCalled = false;
1024 }
1025
1026 /**
Pierre De Ropc8295c22015-06-04 10:15:35 +00001027 * Sets the m_handlingChange flag that indicates if the state machine is currently running the handleChange method.
1028 */
1029 private void handlingChange(boolean transiting) {
1030 m_handlingChange = transiting;
1031 }
1032
1033 /**
1034 * Are we currently running the handleChange method ?
1035 */
1036 private boolean isHandlingChange() {
1037 return m_handlingChange;
1038 }
1039
1040 /**
1041 * Then handleEvent calls this method when a dependency service is being added.
1042 */
Pierre De Rop3a00a212015-03-01 09:27:46 +00001043 private void handleAdded(DependencyContext dc, Event e) {
1044 if (! m_isStarted) {
1045 return;
1046 }
1047 m_logger.debug("handleAdded %s", e);
1048
1049 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
1050 dependencyEvents.add(e);
1051 dc.setAvailable(true);
Pierre De Ropc8295c22015-06-04 10:15:35 +00001052
Pierre De Ropc40d93f2015-05-04 20:25:57 +00001053 // In the following switch block, we sometimes only recalculate state changes
Pierre De Ropd2012be2015-04-28 12:17:36 +00001054 // if the dependency is fully started. If the dependency is not started,
1055 // it means it is actually starting (the service tracker is executing the open method).
Pierre De Ropc8295c22015-06-04 10:15:35 +00001056 // And in this case, depending on the state, we don't recalculate state changes now.
Pierre De Ropd2012be2015-04-28 12:17:36 +00001057 //
Pierre De Rop3a00a212015-03-01 09:27:46 +00001058 // All this is done for two reasons:
Pierre De Ropc40d93f2015-05-04 20:25:57 +00001059 // 1- optimization: it is preferable to recalculate state changes once we know about all currently
1060 // available dependency services (after the tracker has returned from its open method).
Pierre De Ropc8295c22015-06-04 10:15:35 +00001061 // 2- This also allows to determine the list of currently available dependency services before calling
1062 // the component start() callback.
Pierre De Rop3a00a212015-03-01 09:27:46 +00001063
Pierre De Ropd2012be2015-04-28 12:17:36 +00001064 switch (m_state) {
1065 case WAITING_FOR_REQUIRED:
1066 if (dc.isStarted() && dc.isRequired()) {
Pierre De Ropd2012be2015-04-28 12:17:36 +00001067 handleChange();
Pierre De Rop3a00a212015-03-01 09:27:46 +00001068 }
Pierre De Ropd2012be2015-04-28 12:17:36 +00001069 break;
1070 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
1071 if (!dc.isInstanceBound()) {
1072 if (dc.isRequired()) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001073 invokeCallbackSafe(dc, EventType.ADDED, e);
Pierre De Ropd2012be2015-04-28 12:17:36 +00001074 }
1075 updateInstance(dc, e, false, true);
1076 } else {
1077 if (dc.isStarted() && dc.isRequired()) {
Pierre De Ropd2012be2015-04-28 12:17:36 +00001078 handleChange();
1079 }
1080 }
1081 break;
1082 case TRACKING_OPTIONAL:
Pierre De Ropc8295c22015-06-04 10:15:35 +00001083 invokeCallbackSafe(dc, EventType.ADDED, e);
Pierre De Ropd2012be2015-04-28 12:17:36 +00001084 updateInstance(dc, e, false, true);
1085 break;
1086 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +00001087 }
Pierre De Ropc8295c22015-06-04 10:15:35 +00001088 }
1089
1090 /**
1091 * Then handleEvent calls this method when a dependency service is being changed.
1092 */
Pierre De Rop3a00a212015-03-01 09:27:46 +00001093 private void handleChanged(final DependencyContext dc, final Event e) {
1094 if (! m_isStarted) {
1095 return;
1096 }
1097 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
1098 dependencyEvents.remove(e);
1099 dependencyEvents.add(e);
Pierre De Ropc8295c22015-06-04 10:15:35 +00001100
Pierre De Ropd2012be2015-04-28 12:17:36 +00001101 switch (m_state) {
1102 case TRACKING_OPTIONAL:
Pierre De Ropc8295c22015-06-04 10:15:35 +00001103 invokeCallbackSafe(dc, EventType.CHANGED, e);
Pierre De Ropd2012be2015-04-28 12:17:36 +00001104 updateInstance(dc, e, true, false);
1105 break;
1106
1107 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
1108 if (!dc.isInstanceBound()) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001109 invokeCallbackSafe(dc, EventType.CHANGED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001110 updateInstance(dc, e, true, false);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001111 }
Pierre De Ropd2012be2015-04-28 12:17:36 +00001112 break;
1113 default:
1114 // noop
Pierre De Rop3a00a212015-03-01 09:27:46 +00001115 }
1116 }
1117
Pierre De Ropc8295c22015-06-04 10:15:35 +00001118 /**
1119 * Then handleEvent calls this method when a dependency service is being removed.
1120 */
Pierre De Rop3a00a212015-03-01 09:27:46 +00001121 private void handleRemoved(DependencyContext dc, Event e) {
1122 if (! m_isStarted) {
1123 return;
1124 }
1125 // Check if the dependency is still available.
1126 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
1127 int size = dependencyEvents.size();
1128 if (dependencyEvents.contains(e)) {
1129 size--; // the dependency is currently registered and is about to be removed.
1130 }
1131 dc.setAvailable(size > 0);
1132
1133 // If the dependency is now unavailable, we have to recalculate state change. This will result in invoking the
1134 // "removed" callback with the removed dependency (which we have not yet removed from our dependency events list.).
1135 // But we don't recalculate the state if the dependency is not started (if not started, it means that it is currently starting,
1136 // and the tracker is detecting a removed service).
1137 if (size == 0 && dc.isStarted()) {
1138 handleChange();
1139 }
1140
1141 // Now, really remove the dependency event.
1142 dependencyEvents.remove(e);
1143
Pierre De Ropd2012be2015-04-28 12:17:36 +00001144 // Depending on the state, we possible have to invoke the callbacks and update the component instance.
1145 switch (m_state) {
1146 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
1147 if (!dc.isInstanceBound()) {
1148 if (dc.isRequired()) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001149 invokeCallbackSafe(dc, EventType.REMOVED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001150 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001151 updateInstance(dc, e, false, false);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001152 }
Pierre De Ropd2012be2015-04-28 12:17:36 +00001153 break;
1154 case TRACKING_OPTIONAL:
Pierre De Ropc8295c22015-06-04 10:15:35 +00001155 invokeCallbackSafe(dc, EventType.REMOVED, e);
Pierre De Ropd2012be2015-04-28 12:17:36 +00001156 updateInstance(dc, e, false, false);
1157 break;
1158 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +00001159 }
1160 }
1161
1162 private void handleSwapped(DependencyContext dc, Event oldEvent, Event newEvent) {
1163 if (! m_isStarted) {
1164 return;
1165 }
1166 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
1167 dependencyEvents.remove(oldEvent);
1168 dependencyEvents.add(newEvent);
1169
Pierre De Ropd2012be2015-04-28 12:17:36 +00001170 // Depending on the state, we possible have to invoke the callbacks and update the component instance.
1171 switch (m_state) {
1172 case WAITING_FOR_REQUIRED:
1173 // No need to swap, we don't have yet injected anything
1174 break;
1175 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
1176 // Only swap *non* instance-bound dependencies
1177 if (!dc.isInstanceBound()) {
1178 if (dc.isRequired()) {
1179 dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001180 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001181 }
Pierre De Ropd2012be2015-04-28 12:17:36 +00001182 break;
1183 case TRACKING_OPTIONAL:
1184 dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
1185 break;
1186 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +00001187 }
1188 }
Pierre De Ropc8295c22015-06-04 10:15:35 +00001189
Pierre De Rop3a00a212015-03-01 09:27:46 +00001190 private boolean allRequiredAvailable() {
1191 boolean available = true;
1192 for (DependencyContext d : m_dependencies) {
1193 if (d.isRequired() && !d.isInstanceBound()) {
1194 if (!d.isAvailable()) {
1195 available = false;
1196 break;
1197 }
1198 }
1199 }
1200 return available;
1201 }
1202
1203 private boolean allInstanceBoundAvailable() {
1204 boolean available = true;
1205 for (DependencyContext d : m_dependencies) {
1206 if (d.isRequired() && d.isInstanceBound()) {
1207 if (!d.isAvailable()) {
1208 available = false;
1209 break;
1210 }
1211 }
1212 }
1213 return available;
1214 }
1215
1216 private boolean someDependenciesNeedInstance() {
1217 for (DependencyContext d : m_dependencies) {
1218 if (d.needsInstance()) {
1219 return true;
1220 }
1221 }
1222 return false;
1223 }
1224
1225 /**
1226 * Updates the component instance(s).
1227 * @param dc the dependency context for the updating dependency service
1228 * @param event the event holding the updating service (service + properties)
1229 * @param update true if dependency service properties are updating, false if not. If false, it means
1230 * that a dependency service is being added or removed. (see the "add" flag).
1231 * @param add true if the dependency service has been added, false if it has been removed. This flag is
1232 * ignored if the "update" flag is true (because the dependency properties are just being updated).
1233 */
1234 private void updateInstance(DependencyContext dc, Event event, boolean update, boolean add) {
1235 if (dc.isAutoConfig()) {
1236 updateImplementation(dc.getAutoConfigType(), dc, dc.getAutoConfigName(), event, update, add);
1237 }
1238 if (dc.isPropagated() && m_registration != null) {
1239 m_registration.setProperties(calculateServiceProperties());
1240 }
1241 }
1242
1243 private void startDependencies(List<DependencyContext> dependencies) {
1244 // Start first optional dependencies first.
1245 m_logger.debug("startDependencies.");
1246 List<DependencyContext> requiredDeps = new ArrayList<>();
1247 for (DependencyContext d : dependencies) {
1248 if (d.isRequired()) {
1249 requiredDeps.add(d);
1250 continue;
1251 }
1252 if (d.needsInstance()) {
1253 instantiateComponent();
1254 }
1255 d.start();
1256 }
1257 // now, start required dependencies.
1258 for (DependencyContext d : requiredDeps) {
1259 if (d.needsInstance()) {
1260 instantiateComponent();
1261 }
1262 d.start();
1263 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001264 }
1265
1266 private void stopDependencies() {
1267 for (DependencyContext d : m_dependencies) {
1268 d.stop();
1269 }
1270 }
1271
1272 private void registerService() {
1273 if (m_context != null && m_serviceName != null) {
1274 ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
1275 m_registration = wrapper;
1276 autoConfigureImplementation(ServiceRegistration.class, m_registration);
1277
1278 // service name can either be a string or an array of strings
1279 ServiceRegistration registration;
1280
1281 // determine service properties
1282 Dictionary<?,?> properties = calculateServiceProperties();
1283
1284 // register the service
1285 try {
1286 if (m_serviceName instanceof String) {
1287 registration = m_context.registerService((String) m_serviceName, m_componentInstance, properties);
1288 }
1289 else {
1290 registration = m_context.registerService((String[]) m_serviceName, m_componentInstance, properties);
1291 }
1292 wrapper.setServiceRegistration(registration);
1293 }
1294 catch (IllegalArgumentException iae) {
1295 m_logger.log(Logger.LOG_ERROR, "Could not register service " + m_componentInstance, iae);
1296 // set the registration to an illegal state object, which will make all invocations on this
1297 // wrapper fail with an ISE (which also occurs when the SR becomes invalid)
1298 wrapper.setIllegalState();
1299 }
1300 }
1301 }
1302
1303 private void unregisterService() {
1304 if (m_serviceName != null && m_registration != null) {
1305 try {
1306 if (m_bundle != null && m_bundle.getState() == Bundle.ACTIVE) {
1307 m_registration.unregister();
1308 }
1309 } catch (IllegalStateException e) { /* Should we really log this ? */}
1310 autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
1311 m_registration = null;
1312 }
1313 }
1314
1315 private Dictionary<Object, Object> calculateServiceProperties() {
1316 Dictionary<Object, Object> properties = new Hashtable<>();
1317 for (int i = 0; i < m_dependencies.size(); i++) {
1318 DependencyContext d = (DependencyContext) m_dependencies.get(i);
1319 if (d.isPropagated() && d.isAvailable()) {
1320 Dictionary<Object, Object> dict = d.getProperties();
1321 addTo(properties, dict);
1322 }
1323 }
1324 // our service properties must not be overriden by propagated dependency properties, so we add our service
1325 // properties after having added propagated dependency properties.
1326 addTo(properties, m_serviceProperties);
1327 if (properties.size() == 0) {
1328 properties = null;
1329 }
1330 return properties;
1331 }
1332
1333 private void addTo(Dictionary<Object, Object> properties, Dictionary<Object, Object> additional) {
1334 if (properties == null) {
1335 throw new IllegalArgumentException("Dictionary to add to cannot be null.");
1336 }
1337 if (additional != null) {
1338 Enumeration<Object> e = additional.keys();
1339 while (e.hasMoreElements()) {
1340 Object key = e.nextElement();
1341 properties.put(key, additional.get(key));
1342 }
1343 }
1344 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001345
1346 private void destroyComponent() {
1347 m_componentInstance = null;
1348 }
1349
1350 private void invokeAddRequiredDependencies() {
1351 for (DependencyContext d : m_dependencies) {
1352 if (d.isRequired() && !d.isInstanceBound()) {
1353 for (Event e : m_dependencyEvents.get(d)) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001354 invokeCallbackSafe(d, EventType.ADDED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001355 }
1356 }
1357 }
1358 }
1359
1360 private void invokeAutoConfigDependencies() {
1361 for (DependencyContext d : m_dependencies) {
1362 if (d.isAutoConfig() && !d.isInstanceBound()) {
1363 configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
1364 }
1365 }
1366 }
1367
1368 private void invokeAutoConfigInstanceBoundDependencies() {
1369 for (DependencyContext d : m_dependencies) {
1370 if (d.isAutoConfig() && d.isInstanceBound()) {
1371 configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
1372 }
1373 }
1374 }
1375
1376 private void invokeAddRequiredInstanceBoundDependencies() {
1377 for (DependencyContext d : m_dependencies) {
1378 if (d.isRequired() && d.isInstanceBound()) {
1379 for (Event e : m_dependencyEvents.get(d)) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001380 invokeCallbackSafe(d, EventType.ADDED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001381 }
1382 }
1383 }
1384 }
1385
1386 private void invokeAddOptionalDependencies() {
1387 for (DependencyContext d : m_dependencies) {
1388 if (! d.isRequired()) {
1389 for (Event e : m_dependencyEvents.get(d)) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001390 invokeCallbackSafe(d, EventType.ADDED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001391 }
1392 }
1393 }
1394 }
1395
1396 private void invokeRemoveRequiredDependencies() {
1397 for (DependencyContext d : m_dependencies) {
1398 if (!d.isInstanceBound() && d.isRequired()) {
1399 for (Event e : m_dependencyEvents.get(d)) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001400 invokeCallbackSafe(d, EventType.REMOVED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001401 }
1402 }
1403 }
1404 }
1405
1406 private void invokeRemoveOptionalDependencies() {
1407 for (DependencyContext d : m_dependencies) {
1408 if (! d.isRequired()) {
1409 for (Event e : m_dependencyEvents.get(d)) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001410 invokeCallbackSafe(d, EventType.REMOVED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001411 }
1412 }
1413 }
1414 }
1415
1416 private void invokeRemoveInstanceBoundDependencies() {
1417 for (DependencyContext d : m_dependencies) {
1418 if (d.isInstanceBound()) {
1419 for (Event e : m_dependencyEvents.get(d)) {
Pierre De Ropc8295c22015-06-04 10:15:35 +00001420 invokeCallbackSafe(d, EventType.REMOVED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +00001421 }
1422 }
1423 }
1424 }
Pierre De Ropc8295c22015-06-04 10:15:35 +00001425
1426 /**
1427 * This method ensures that a dependency callback is invoked only one time;
Pierre De Rop1eb4a452015-09-18 22:09:45 +00001428 * It also ensures that if the dependency callback is optional, then we only
1429 * invoke the bind method if the component start callback has already been called.
Pierre De Ropc8295c22015-06-04 10:15:35 +00001430 */
1431 private void invokeCallbackSafe(DependencyContext dc, EventType type, Event event) {
Pierre De Rop1eb4a452015-09-18 22:09:45 +00001432 if (! dc.isRequired() && ! m_startCalled) {
1433 return;
1434 }
1435 if (m_invokeCallbackCache.put(event, event) == null) {
1436 dc.invokeCallback(type, event);
1437 }
Pierre De Ropc8295c22015-06-04 10:15:35 +00001438 }
1439
1440 /**
Pierre De Rop1088d6d2015-09-24 20:58:58 +00001441 * Removes and closes all instance bound dependencies.
1442 * This method is called when a component is destroyed.
1443 */
1444 private void removeInstanceBoundDependencies() {
1445 for (DependencyContext dep : m_dependencies) {
1446 if (dep.isInstanceBound()) {
1447 m_dependencies.remove(dep);
1448 dep.stop();
1449 }
1450 }
1451 }
1452
1453 /**
Pierre De Ropc8295c22015-06-04 10:15:35 +00001454 * Clears the cache of invoked components callbacks.
1455 * We only clear the cache when the state machine is not running.
1456 * The cache is used to avoid calling the same bind callback twice.
1457 * See FELIX-4913.
1458 */
1459 private void clearInvokeCallbackCache() {
1460 if (! isHandlingChange()) {
1461 m_invokeCallbackCache.clear();
1462 }
1463 }
Pierre De Rop3a00a212015-03-01 09:27:46 +00001464
1465 private void invoke(String name) {
1466 if (name != null) {
1467 // if a callback instance was specified, look for the method there, if not,
1468 // ask the service for its composition instances
1469 Object[] instances = m_callbackInstance != null ? new Object[] { m_callbackInstance } : getCompositionInstances();
1470
1471 long t1 = System.nanoTime();
1472 try {
1473 invokeCallbackMethod(instances, name,
1474 new Class[][] {{ Component.class }, {}},
1475 new Object[][] {{ this }, {}},
1476 false);
1477 } finally {
1478 long t2 = System.nanoTime();
1479 m_stopwatch.put(name, t2 - t1);
1480 }
1481 }
1482 }
1483
Pierre De Ropc8295c22015-06-04 10:15:35 +00001484 private Object createInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Pierre De Rop3a00a212015-03-01 09:27:46 +00001485 Constructor<?> constructor = clazz.getConstructor(VOID);
1486 constructor.setAccessible(true);
1487 return constructor.newInstance();
1488 }
1489
1490 private void notifyListeners(ComponentState state) {
1491 for (ComponentStateListener l : m_listeners) {
1492 l.changed(this, state);
1493 }
1494 }
1495
Pierre De Rop3a00a212015-03-01 09:27:46 +00001496 private void autoConfigureImplementation(Class<?> clazz, Object instance) {
1497 if (((Boolean) m_autoConfig.get(clazz)).booleanValue()) {
1498 configureImplementation(clazz, instance, (String) m_autoConfigInstance.get(clazz));
1499 }
1500 }
1501
1502 /**
1503 * Configure a field in the service implementation. The service implementation
1504 * is searched for fields that have the same type as the class that was specified
1505 * and for each of these fields, the specified instance is filled in.
1506 *
1507 * @param clazz the class to search for
1508 * @param instance the object to fill in the implementation class(es) field
1509 * @param instanceName the name of the instance to fill in, or <code>null</code> if not used
1510 */
1511 private void configureImplementation(Class<?> clazz, Object instance, String fieldName) {
1512 Object[] targets = getInstances();
1513 if (! FieldUtil.injectField(targets, fieldName, clazz, instance, m_logger) && fieldName != null) {
1514 // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
1515 // if field has not been injected.
1516 if (! (getInstance() instanceof AbstractDecorator)) {
1517 m_logger.log(Logger.LOG_ERROR, "Could not inject " + instance + " to field \"" + fieldName
1518 + "\" at any of the following component instances: " + Arrays.toString(targets));
1519 }
1520 }
1521 }
1522
1523 private void configureImplementation(Class<?> clazz, DependencyContext dc, String fieldName) {
1524 Object[] targets = getInstances();
1525 if (! FieldUtil.injectDependencyField(targets, fieldName, clazz, dc, m_logger) && fieldName != null) {
1526 // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
1527 // if field has not been injected.
1528 if (! (getInstance() instanceof AbstractDecorator)) {
1529 m_logger.log(Logger.LOG_ERROR, "Could not inject dependency " + clazz.getName() + " to field \""
1530 + fieldName + "\" at any of the following component instances: " + Arrays.toString(targets));
1531 }
1532 }
1533 }
1534
1535 /**
1536 * Update the component instances.
1537 *
1538 * @param clazz the class of the dependency service to inject in the component instances
1539 * @param dc the dependency context for the updating dependency service
1540 * @param fieldName the component instances fieldname to fill in with the updated dependency service
1541 * @param event the event holding the updating service (service + properties)
1542 * @param update true if dependency service properties are updating, false if not. If false, it means
1543 * that a dependency service is being added or removed. (see the "add" flag).
1544 * @param add true if the dependency service has been added, false if it has been removed. This flag is
1545 * ignored if the "update" flag is true (because the dependency properties are just being updated).
1546 */
1547 private void updateImplementation(Class<?> clazz, DependencyContext dc, String fieldName, Event event, boolean update,
1548 boolean add)
1549 {
1550 Object[] targets = getInstances();
1551 FieldUtil.updateDependencyField(targets, fieldName, update, add, clazz, event, dc, m_logger);
1552 }
1553
Pierre De Rop3a00a212015-03-01 09:27:46 +00001554 private Object[] getCompositionInstances() {
1555 Object[] instances = null;
1556 if (m_compositionManagerGetMethod != null) {
1557 if (m_compositionManager != null) {
1558 m_compositionManagerInstance = m_compositionManager;
1559 }
1560 else {
1561 m_compositionManagerInstance = m_componentInstance;
1562 }
1563 if (m_compositionManagerInstance != null) {
1564 try {
1565 instances = (Object[]) InvocationUtil.invokeMethod(m_compositionManagerInstance, m_compositionManagerInstance.getClass(), m_compositionManagerGetMethod, new Class[][] {{}}, new Object[][] {{}}, false);
1566 }
1567 catch (Exception e) {
1568 m_logger.log(Logger.LOG_ERROR, "Could not obtain instances from the composition manager.", e);
1569 instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
1570 }
1571 }
1572 }
1573 else {
1574 instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
1575 }
1576 return instances;
1577 }
1578
Pierre De Rop3a00a212015-03-01 09:27:46 +00001579 private void appendProperties(StringBuffer result) {
1580 Dictionary<Object, Object> properties = calculateServiceProperties();
1581 if (properties != null) {
1582 result.append("(");
1583 Enumeration<?> enumeration = properties.keys();
1584 while (enumeration.hasMoreElements()) {
1585 Object key = enumeration.nextElement();
1586 result.append(key.toString());
1587 result.append('=');
1588 Object value = properties.get(key);
1589 if (value instanceof String[]) {
1590 String[] values = (String[]) value;
1591 result.append('{');
1592 for (int i = 0; i < values.length; i++) {
1593 if (i > 0) {
1594 result.append(',');
1595 }
1596 result.append(values[i].toString());
1597 }
1598 result.append('}');
1599 }
1600 else {
1601 result.append(value.toString());
1602 }
1603 if (enumeration.hasMoreElements()) {
1604 result.append(',');
1605 }
1606 }
1607 result.append(")");
1608 }
1609 }
1610
Pierre De Rop3a00a212015-03-01 09:27:46 +00001611 private Executor getExecutor() {
1612 return m_executor;
1613 }
1614}