blob: 63bfe70f4ad5e4a8eab0e1ee3c7f48ba109e313e [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;
36import java.util.List;
37import java.util.Map;
38import java.util.Set;
39import java.util.concurrent.ConcurrentHashMap;
40import java.util.concurrent.ConcurrentSkipListSet;
41import java.util.concurrent.CopyOnWriteArrayList;
42import java.util.concurrent.Executor;
43import java.util.concurrent.atomic.AtomicBoolean;
44import java.util.concurrent.atomic.AtomicLong;
45
46import org.apache.felix.dm.Component;
47import org.apache.felix.dm.ComponentDeclaration;
48import org.apache.felix.dm.ComponentDependencyDeclaration;
49import org.apache.felix.dm.ComponentState;
50import org.apache.felix.dm.ComponentStateListener;
51import org.apache.felix.dm.Dependency;
52import org.apache.felix.dm.DependencyManager;
53import org.apache.felix.dm.Logger;
54import org.apache.felix.dm.context.ComponentContext;
55import org.apache.felix.dm.context.DependencyContext;
56import org.apache.felix.dm.context.EventType;
57import org.apache.felix.dm.context.Event;
58import org.osgi.framework.Bundle;
59import org.osgi.framework.BundleContext;
60import org.osgi.framework.ServiceRegistration;
61import org.osgi.service.log.LogService;
62
63/**
64 * Dependency Manager Component implementation.
65 *
66 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
67 */
68public class ComponentImpl implements Component, ComponentContext, ComponentDeclaration {
69 private static final ServiceRegistration NULL_REGISTRATION = (ServiceRegistration) Proxy
70 .newProxyInstance(ComponentImpl.class.getClassLoader(),
71 new Class[] { ServiceRegistration.class },
72 new DefaultNullObject());
73 private static final Class<?>[] VOID = new Class[] {};
74 private volatile Executor m_executor = new SerialExecutor(new Logger(null));
75 private ComponentState m_state = ComponentState.INACTIVE;
76 private final CopyOnWriteArrayList<DependencyContext> m_dependencies = new CopyOnWriteArrayList<>();
77 private final List<ComponentStateListener> m_listeners = new CopyOnWriteArrayList<>();
78 private boolean m_isStarted;
79 private final Logger m_logger;
80 private final BundleContext m_context;
81 private final DependencyManager m_manager;
82 private Object m_componentDefinition;
83 private Object m_componentInstance;
84 private volatile Object m_serviceName;
85 private volatile Dictionary<Object, Object> m_serviceProperties;
86 private volatile ServiceRegistration m_registration;
87 private final Map<Class<?>, Boolean> m_autoConfig = new ConcurrentHashMap<>();
88 private final Map<Class<?>, String> m_autoConfigInstance = new ConcurrentHashMap<>();
89 private final Map<String, Long> m_stopwatch = new ConcurrentHashMap<>();
90 private final long m_id;
91 private static AtomicLong m_idGenerator = new AtomicLong();
92 // Holds all the services of a given dependency context. Caution: the last entry in the skiplist is the highest ranked service.
93 private final Map<DependencyContext, ConcurrentSkipListSet<Event>> m_dependencyEvents = new HashMap<>();
94 private final AtomicBoolean m_active = new AtomicBoolean(false);
95
96 public Component setDebug(String debugKey) {
97 // Force debug level in our logger
98 m_logger.setEnabledLevel(LogService.LOG_DEBUG);
99 m_logger.setDebugKey(debugKey);
100 return this;
101 }
102
103 // configuration (static)
104 private volatile String m_callbackInit;
105 private volatile String m_callbackStart;
106 private volatile String m_callbackStop;
107 private volatile String m_callbackDestroy;
108 private volatile Object m_callbackInstance;
109
110 // instance factory
111 private volatile Object m_instanceFactory;
112 private volatile String m_instanceFactoryCreateMethod;
113
114 // composition manager
115 private volatile Object m_compositionManager;
116 private volatile String m_compositionManagerGetMethod;
117 private volatile Object m_compositionManagerInstance;
118 private final Bundle m_bundle;
119
120 static class SCDImpl implements ComponentDependencyDeclaration {
121 private final String m_name;
122 private final int m_state;
123 private final String m_type;
124
125 public SCDImpl(String name, int state, String type) {
126 m_name = name;
127 m_state = state;
128 m_type = type;
129 }
130
131 public String getName() {
132 return m_name;
133 }
134
135 public String getSimpleName() {
136 return m_name;
137 }
138
139 public String getFilter() {
140 return null;
141 }
142
143 public int getState() {
144 return m_state;
145 }
146
147 public String getType() {
148 return m_type;
149 }
150 }
151
152 public ComponentImpl() {
153 this(null, null, new Logger(null));
154 }
155
156 public ComponentImpl(BundleContext context, DependencyManager manager, Logger logger) {
157 m_context = context;
158 m_bundle = context != null ? context.getBundle() : null;
159 m_manager = manager;
160 m_logger = logger;
161 m_autoConfig.put(BundleContext.class, Boolean.TRUE);
162 m_autoConfig.put(ServiceRegistration.class, Boolean.TRUE);
163 m_autoConfig.put(DependencyManager.class, Boolean.TRUE);
164 m_autoConfig.put(Component.class, Boolean.TRUE);
165 m_callbackInit = "init";
166 m_callbackStart = "start";
167 m_callbackStop = "stop";
168 m_callbackDestroy = "destroy";
169 m_id = m_idGenerator.getAndIncrement();
170 }
171
172 @Override
173 public Component add(final Dependency ... dependencies) {
174 getExecutor().execute(new Runnable() {
175 @Override
176 public void run() {
177 List<DependencyContext> instanceBoundDeps = new ArrayList<>();
178 for (Dependency d : dependencies) {
179 DependencyContext dc = (DependencyContext) d;
180 if (dc.getComponentContext() != null) {
181 m_logger.err("%s can't be added to %s (dependency already added to another component).", dc,
182 ComponentImpl.this);
183 continue;
184 }
185 m_dependencyEvents.put(dc, new ConcurrentSkipListSet<Event>());
186 m_dependencies.add(dc);
187 dc.setComponentContext(ComponentImpl.this);
188 if (!(m_state == ComponentState.INACTIVE)) {
189 dc.setInstanceBound(true);
190 instanceBoundDeps.add(dc);
191 }
192 }
193 startDependencies(instanceBoundDeps);
194 handleChange();
195 }
196 });
197 return this;
198 }
199
200 @Override
201 public Component remove(final Dependency d) {
202 getExecutor().execute(new Runnable() {
203 @Override
204 public void run() {
205 DependencyContext dc = (DependencyContext) d;
206 // First remove this dependency from the dependency list
207 m_dependencies.remove(d);
208 // Now we can stop the dependency (our component won't be deactivated, it will only be unbound with
209 // the removed dependency).
210 if (!(m_state == ComponentState.INACTIVE)) {
211 dc.stop();
212 }
213 // Finally, cleanup the dependency events.
214 m_dependencyEvents.remove(d);
215 handleChange();
216 }
217 });
218 return this;
219 }
220
221 public void start() {
222 if (m_active.compareAndSet(false, true)) {
223 getExecutor().execute(new Runnable() {
224 @Override
225 public void run() {
226 m_isStarted = true;
227 handleChange();
228 }
229 });
230 }
231 }
232
233 public void stop() {
234 if (m_active.compareAndSet(true, false)) {
235 Executor executor = getExecutor();
236
237 // First, declare the task that will stop our component in our executor.
238 final Runnable stopTask = new Runnable() {
239 @Override
240 public void run() {
241 m_isStarted = false;
242 handleChange();
243 }
244 };
245
246 // Now, we have to schedule our stopTask in our component executor. But we have to handle a special case:
247 // if the component bundle is stopping *AND* if the executor is a parallel dispatcher, then we want
248 // to invoke our stopTask synchronously, in order to make sure that the bundle context is valid while our
249 // component is being deactivated (if we stop the component asynchronously, the bundle context may be invalidated
250 // before our component is stopped, and we don't want to be in this situation).
251
252 boolean stopping = m_bundle != null /* null only in tests env */ && m_bundle.getState() == Bundle.STOPPING;
253 if (stopping && executor instanceof DispatchExecutor) {
254 ((DispatchExecutor) executor).execute(stopTask, false /* try to execute synchronously, not using threadpool */);
255 } else {
256 executor.execute(stopTask);
257 }
258 }
259 }
260
261 @SuppressWarnings("unchecked")
262 @Override
263 public Component setInterface(String serviceName, Dictionary<?, ?> properties) {
264 ensureNotActive();
265 m_serviceName = serviceName;
266 m_serviceProperties = (Dictionary<Object, Object>) properties;
267 return this;
268 }
269
270 @SuppressWarnings("unchecked")
271 @Override
272 public Component setInterface(String[] serviceName, Dictionary<?, ?> properties) {
273 ensureNotActive();
274 m_serviceName = serviceName;
275 m_serviceProperties = (Dictionary<Object, Object>) properties;
276 return this;
277 }
278
279 @Override
280 public void handleEvent(final DependencyContext dc, final EventType type, final Event... event) {
281 // since this method can be invoked by anyone from any thread, we need to
282 // pass on the event to a runnable that we execute using the component's
283 // executor
284 getExecutor().execute(new Runnable() {
285 @Override
286 public void run() {
287 switch (type) {
288 case ADDED:
289 handleAdded(dc, event[0]);
290 break;
291 case CHANGED:
292 handleChanged(dc, event[0]);
293 break;
294 case REMOVED:
295 handleRemoved(dc, event[0]);
296 break;
297 case SWAPPED:
298 handleSwapped(dc, event[0], event[1]);
299 break;
300 }
301 }
302 });
303 }
304
305 @Override
306 public Event getDependencyEvent(DependencyContext dc) {
307 ConcurrentSkipListSet<Event> events = m_dependencyEvents.get(dc);
308 return events.size() > 0 ? events.last() : null;
309 }
310
311 @Override
312 public Set<Event> getDependencyEvents(DependencyContext dc) {
313 return m_dependencyEvents.get(dc);
314 }
315
316 public Component setAutoConfig(Class<?> clazz, boolean autoConfig) {
317 m_autoConfig.put(clazz, Boolean.valueOf(autoConfig));
318 return this;
319 }
320
321 public Component setAutoConfig(Class<?> clazz, String instanceName) {
322 m_autoConfig.put(clazz, Boolean.valueOf(instanceName != null));
323 m_autoConfigInstance.put(clazz, instanceName);
324 return this;
325 }
326
327 public boolean getAutoConfig(Class<?> clazz) {
328 Boolean result = (Boolean) m_autoConfig.get(clazz);
329 return (result != null && result.booleanValue());
330 }
331
332 public String getAutoConfigInstance(Class<?> clazz) {
333 return (String) m_autoConfigInstance.get(clazz);
334 }
335
336 private void handleAdded(DependencyContext dc, Event e) {
337 if (! m_isStarted) {
338 return;
339 }
340 m_logger.debug("handleAdded %s", e);
341
342 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
343 dependencyEvents.add(e);
344 dc.setAvailable(true);
345
Pierre De Ropd2012be2015-04-28 12:17:36 +0000346 // In the following switch block, we only recalculate state changes
347 // if the dependency is fully started. If the dependency is not started,
348 // it means it is actually starting (the service tracker is executing the open method).
349 // And in this case, we don't recalculate state changes now. We'll do it
350 // once all currently available services are found, and then after,
351 // we'll recalculate state change (see the startDependencies method).
352 //
Pierre De Rop3a00a212015-03-01 09:27:46 +0000353 // All this is done for two reasons:
354 // 1- optimization: it is preferable to recalculate state changes once we know about all currently available dependency services
355 // (after the tracker has returned from its open method).
356 // 2- This also allows to determine the list of currently available dependency services from within the component start method callback
357 // (this will be extremely useful when porting the Felix SCR on top of DM4).
358
Pierre De Ropd2012be2015-04-28 12:17:36 +0000359 switch (m_state) {
360 case WAITING_FOR_REQUIRED:
361 if (dc.isStarted() && dc.isRequired()) {
362 // if dependency is starting, we'll handle change after the tracker has returned (see startDependencies method).
363 handleChange();
Pierre De Rop3a00a212015-03-01 09:27:46 +0000364 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000365 break;
366 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
367 if (!dc.isInstanceBound()) {
368 if (dc.isRequired()) {
369 dc.invokeCallback(EventType.ADDED, e);
370 }
371 updateInstance(dc, e, false, true);
372 } else {
373 if (dc.isStarted() && dc.isRequired()) {
374 // if dependency is starting, we'll handle change after the tracker has returned (see startDependencies method).
375 handleChange();
376 }
377 }
378 break;
379 case TRACKING_OPTIONAL:
380 dc.invokeCallback(EventType.ADDED, e);
381 updateInstance(dc, e, false, true);
382 break;
383 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +0000384 }
385 }
386
387 private void handleChanged(final DependencyContext dc, final Event e) {
388 if (! m_isStarted) {
389 return;
390 }
391 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
392 dependencyEvents.remove(e);
393 dependencyEvents.add(e);
394
Pierre De Ropd2012be2015-04-28 12:17:36 +0000395 switch (m_state) {
396 case TRACKING_OPTIONAL:
397 dc.invokeCallback(EventType.CHANGED, e);
398 updateInstance(dc, e, true, false);
399 break;
400
401 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
402 if (!dc.isInstanceBound()) {
Pierre De Rop3a00a212015-03-01 09:27:46 +0000403 dc.invokeCallback(EventType.CHANGED, e);
404 updateInstance(dc, e, true, false);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000405 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000406 break;
407 default:
408 // noop
Pierre De Rop3a00a212015-03-01 09:27:46 +0000409 }
410 }
411
412 private void handleRemoved(DependencyContext dc, Event e) {
413 if (! m_isStarted) {
414 return;
415 }
416 // Check if the dependency is still available.
417 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
418 int size = dependencyEvents.size();
419 if (dependencyEvents.contains(e)) {
420 size--; // the dependency is currently registered and is about to be removed.
421 }
422 dc.setAvailable(size > 0);
423
424 // If the dependency is now unavailable, we have to recalculate state change. This will result in invoking the
425 // "removed" callback with the removed dependency (which we have not yet removed from our dependency events list.).
426 // But we don't recalculate the state if the dependency is not started (if not started, it means that it is currently starting,
427 // and the tracker is detecting a removed service).
428 if (size == 0 && dc.isStarted()) {
429 handleChange();
430 }
431
432 // Now, really remove the dependency event.
433 dependencyEvents.remove(e);
434
Pierre De Ropd2012be2015-04-28 12:17:36 +0000435 // Depending on the state, we possible have to invoke the callbacks and update the component instance.
436 switch (m_state) {
437 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
438 if (!dc.isInstanceBound()) {
439 if (dc.isRequired()) {
440 dc.invokeCallback(EventType.REMOVED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000441 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000442 updateInstance(dc, e, false, false);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000443 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000444 break;
445 case TRACKING_OPTIONAL:
446 dc.invokeCallback(EventType.REMOVED, e);
447 updateInstance(dc, e, false, false);
448 break;
449 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +0000450 }
451 }
452
453 private void handleSwapped(DependencyContext dc, Event oldEvent, Event newEvent) {
454 if (! m_isStarted) {
455 return;
456 }
457 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
458 dependencyEvents.remove(oldEvent);
459 dependencyEvents.add(newEvent);
460
Pierre De Ropd2012be2015-04-28 12:17:36 +0000461 // Depending on the state, we possible have to invoke the callbacks and update the component instance.
462 switch (m_state) {
463 case WAITING_FOR_REQUIRED:
464 // No need to swap, we don't have yet injected anything
465 break;
466 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
467 // Only swap *non* instance-bound dependencies
468 if (!dc.isInstanceBound()) {
469 if (dc.isRequired()) {
470 dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000471 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000472 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000473 break;
474 case TRACKING_OPTIONAL:
475 dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
476 break;
477 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +0000478 }
479 }
480
481 private void handleChange() {
482 m_logger.debug("handleChanged");
483 try {
484 ComponentState oldState;
485 ComponentState newState;
486 do {
487 oldState = m_state;
488 newState = calculateNewState(oldState);
489 m_logger.debug("%s -> %s", oldState, newState);
490 m_state = newState;
491 } while (performTransition(oldState, newState));
492 } finally {
493 m_logger.debug("end handling change.");
494 }
495 }
496
497 /** Based on the current state, calculate the new state. */
498 private ComponentState calculateNewState(ComponentState currentState) {
499 if (currentState == INACTIVE) {
500 if (m_isStarted) {
501 return WAITING_FOR_REQUIRED;
502 }
503 }
504 if (currentState == WAITING_FOR_REQUIRED) {
505 if (!m_isStarted) {
506 return INACTIVE;
507 }
508 if (allRequiredAvailable()) {
509 return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
510 }
511 }
512 if (currentState == INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
513 if (m_isStarted && allRequiredAvailable()) {
514 if (allInstanceBoundAvailable()) {
515 return TRACKING_OPTIONAL;
516 }
517 return currentState;
518 }
519 return WAITING_FOR_REQUIRED;
520 }
521 if (currentState == TRACKING_OPTIONAL) {
522 if (m_isStarted && allRequiredAvailable() && allInstanceBoundAvailable()) {
523 return currentState;
524 }
525 return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
526 }
527 return currentState;
528 }
529
530 /** Perform all the actions associated with state transitions. Returns true if a transition was performed. */
531 private boolean performTransition(ComponentState oldState, ComponentState newState) {
532// System.out.println("transition from " + oldState + " to " + newState);
533 if (oldState == ComponentState.INACTIVE && newState == ComponentState.WAITING_FOR_REQUIRED) {
534 startDependencies(m_dependencies);
535 notifyListeners(newState);
536 return true;
537 }
538 if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
539 instantiateComponent();
540 invokeAutoConfigDependencies();
541 invokeAddRequiredDependencies();
542 ComponentState stateBeforeCallingInit = m_state;
543 invoke(m_callbackInit);
544 if (stateBeforeCallingInit == m_state) {
545 notifyListeners(newState); // init did not change current state, we can notify about this new state
546 }
547 return true;
548 }
549 if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.TRACKING_OPTIONAL) {
550 invokeAutoConfigInstanceBoundDependencies();
551 invokeAddRequiredInstanceBoundDependencies();
552 invoke(m_callbackStart);
553 invokeAddOptionalDependencies();
554 registerService();
555 notifyListeners(newState);
556 return true;
557 }
558 if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
559 unregisterService();
560 invokeRemoveOptionalDependencies();
561 invoke(m_callbackStop);
562 invokeRemoveInstanceBoundDependencies();
563 notifyListeners(newState);
564 return true;
565 }
566 if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.WAITING_FOR_REQUIRED) {
567 invoke(m_callbackDestroy);
568 invokeRemoveRequiredDependencies();
569 notifyListeners(newState);
570 if (! someDependenciesNeedInstance()) {
571 destroyComponent();
572 }
573 return true;
574 }
575 if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INACTIVE) {
576 stopDependencies();
577 destroyComponent();
578 notifyListeners(newState);
579 return true;
580 }
581 return false;
582 }
583
584 private boolean allRequiredAvailable() {
585 boolean available = true;
586 for (DependencyContext d : m_dependencies) {
587 if (d.isRequired() && !d.isInstanceBound()) {
588 if (!d.isAvailable()) {
589 available = false;
590 break;
591 }
592 }
593 }
594 return available;
595 }
596
597 private boolean allInstanceBoundAvailable() {
598 boolean available = true;
599 for (DependencyContext d : m_dependencies) {
600 if (d.isRequired() && d.isInstanceBound()) {
601 if (!d.isAvailable()) {
602 available = false;
603 break;
604 }
605 }
606 }
607 return available;
608 }
609
610 private boolean someDependenciesNeedInstance() {
611 for (DependencyContext d : m_dependencies) {
612 if (d.needsInstance()) {
613 return true;
614 }
615 }
616 return false;
617 }
618
619 /**
620 * Updates the component instance(s).
621 * @param dc the dependency context for the updating dependency service
622 * @param event the event holding the updating service (service + properties)
623 * @param update true if dependency service properties are updating, false if not. If false, it means
624 * that a dependency service is being added or removed. (see the "add" flag).
625 * @param add true if the dependency service has been added, false if it has been removed. This flag is
626 * ignored if the "update" flag is true (because the dependency properties are just being updated).
627 */
628 private void updateInstance(DependencyContext dc, Event event, boolean update, boolean add) {
629 if (dc.isAutoConfig()) {
630 updateImplementation(dc.getAutoConfigType(), dc, dc.getAutoConfigName(), event, update, add);
631 }
632 if (dc.isPropagated() && m_registration != null) {
633 m_registration.setProperties(calculateServiceProperties());
634 }
635 }
636
637 private void startDependencies(List<DependencyContext> dependencies) {
638 // Start first optional dependencies first.
639 m_logger.debug("startDependencies.");
640 List<DependencyContext> requiredDeps = new ArrayList<>();
641 for (DependencyContext d : dependencies) {
642 if (d.isRequired()) {
643 requiredDeps.add(d);
644 continue;
645 }
646 if (d.needsInstance()) {
647 instantiateComponent();
648 }
649 d.start();
650 }
651 // now, start required dependencies.
652 for (DependencyContext d : requiredDeps) {
653 if (d.needsInstance()) {
654 instantiateComponent();
655 }
656 d.start();
657 }
658 // The started dependencies has probably called our handleAdded method: we now have to run our state machine.
659 handleChange();
660 }
661
662 private void stopDependencies() {
663 for (DependencyContext d : m_dependencies) {
664 d.stop();
665 }
666 }
667
668 private void registerService() {
669 if (m_context != null && m_serviceName != null) {
670 ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
671 m_registration = wrapper;
672 autoConfigureImplementation(ServiceRegistration.class, m_registration);
673
674 // service name can either be a string or an array of strings
675 ServiceRegistration registration;
676
677 // determine service properties
678 Dictionary<?,?> properties = calculateServiceProperties();
679
680 // register the service
681 try {
682 if (m_serviceName instanceof String) {
683 registration = m_context.registerService((String) m_serviceName, m_componentInstance, properties);
684 }
685 else {
686 registration = m_context.registerService((String[]) m_serviceName, m_componentInstance, properties);
687 }
688 wrapper.setServiceRegistration(registration);
689 }
690 catch (IllegalArgumentException iae) {
691 m_logger.log(Logger.LOG_ERROR, "Could not register service " + m_componentInstance, iae);
692 // set the registration to an illegal state object, which will make all invocations on this
693 // wrapper fail with an ISE (which also occurs when the SR becomes invalid)
694 wrapper.setIllegalState();
695 }
696 }
697 }
698
699 private void unregisterService() {
700 if (m_serviceName != null && m_registration != null) {
701 try {
702 if (m_bundle != null && m_bundle.getState() == Bundle.ACTIVE) {
703 m_registration.unregister();
704 }
705 } catch (IllegalStateException e) { /* Should we really log this ? */}
706 autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
707 m_registration = null;
708 }
709 }
710
711 private Dictionary<Object, Object> calculateServiceProperties() {
712 Dictionary<Object, Object> properties = new Hashtable<>();
713 for (int i = 0; i < m_dependencies.size(); i++) {
714 DependencyContext d = (DependencyContext) m_dependencies.get(i);
715 if (d.isPropagated() && d.isAvailable()) {
716 Dictionary<Object, Object> dict = d.getProperties();
717 addTo(properties, dict);
718 }
719 }
720 // our service properties must not be overriden by propagated dependency properties, so we add our service
721 // properties after having added propagated dependency properties.
722 addTo(properties, m_serviceProperties);
723 if (properties.size() == 0) {
724 properties = null;
725 }
726 return properties;
727 }
728
729 private void addTo(Dictionary<Object, Object> properties, Dictionary<Object, Object> additional) {
730 if (properties == null) {
731 throw new IllegalArgumentException("Dictionary to add to cannot be null.");
732 }
733 if (additional != null) {
734 Enumeration<Object> e = additional.keys();
735 while (e.hasMoreElements()) {
736 Object key = e.nextElement();
737 properties.put(key, additional.get(key));
738 }
739 }
740 }
741
Pierre De Ropd2012be2015-04-28 12:17:36 +0000742 void instantiateComponent() {
Pierre De Rop3a00a212015-03-01 09:27:46 +0000743 m_logger.debug("instantiating component.");
744
745 // TODO add more complex factory instantiations of one or more components in a composition here
746 if (m_componentInstance == null) {
747 if (m_componentDefinition instanceof Class) {
748 try {
749 m_componentInstance = createInstance((Class<?>) m_componentDefinition);
750 }
751 catch (Exception e) {
752 m_logger.log(Logger.LOG_ERROR, "Could not instantiate class " + m_componentDefinition, e);
753 }
754 }
755 else {
756 if (m_instanceFactoryCreateMethod != null) {
757 Object factory = null;
758 if (m_instanceFactory != null) {
759 if (m_instanceFactory instanceof Class) {
760 try {
761 factory = createInstance((Class<?>) m_instanceFactory);
762 }
763 catch (Exception e) {
764 m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
765 }
766 }
767 else {
768 factory = m_instanceFactory;
769 }
770 }
771 else {
772 // TODO review if we want to try to default to something if not specified
773 // for now the JavaDoc of setFactory(method) reflects the fact that we need
774 // to review it
775 }
776 if (factory == null) {
777 m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
778 }
779 else {
780 try {
781 m_componentInstance = InvocationUtil.invokeMethod(factory, factory.getClass(), m_instanceFactoryCreateMethod, new Class[][] {{}}, new Object[][] {{}}, false);
782 }
783 catch (Exception e) {
784 m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
785 }
786 }
787 }
788 }
789
790 if (m_componentInstance == null) {
791 m_componentInstance = m_componentDefinition;
792 }
793
794 // configure the bundle context
795 autoConfigureImplementation(BundleContext.class, m_context);
796 autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
797 autoConfigureImplementation(DependencyManager.class, m_manager);
798 autoConfigureImplementation(Component.class, this);
799 }
800 }
801
802 private void destroyComponent() {
803 m_componentInstance = null;
804 }
805
806 private void invokeAddRequiredDependencies() {
807 for (DependencyContext d : m_dependencies) {
808 if (d.isRequired() && !d.isInstanceBound()) {
809 for (Event e : m_dependencyEvents.get(d)) {
810 d.invokeCallback(EventType.ADDED, e);
811 }
812 }
813 }
814 }
815
816 private void invokeAutoConfigDependencies() {
817 for (DependencyContext d : m_dependencies) {
818 if (d.isAutoConfig() && !d.isInstanceBound()) {
819 configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
820 }
821 }
822 }
823
824 private void invokeAutoConfigInstanceBoundDependencies() {
825 for (DependencyContext d : m_dependencies) {
826 if (d.isAutoConfig() && d.isInstanceBound()) {
827 configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
828 }
829 }
830 }
831
832 private void invokeAddRequiredInstanceBoundDependencies() {
833 for (DependencyContext d : m_dependencies) {
834 if (d.isRequired() && d.isInstanceBound()) {
835 for (Event e : m_dependencyEvents.get(d)) {
836 d.invokeCallback(EventType.ADDED, e);
837 }
838 }
839 }
840 }
841
842 private void invokeAddOptionalDependencies() {
843 for (DependencyContext d : m_dependencies) {
844 if (! d.isRequired()) {
845 for (Event e : m_dependencyEvents.get(d)) {
846 d.invokeCallback(EventType.ADDED, e);
847 }
848 }
849 }
850 }
851
852 private void invokeRemoveRequiredDependencies() {
853 for (DependencyContext d : m_dependencies) {
854 if (!d.isInstanceBound() && d.isRequired()) {
855 for (Event e : m_dependencyEvents.get(d)) {
856 d.invokeCallback(EventType.REMOVED, e);
857 }
858 }
859 }
860 }
861
862 private void invokeRemoveOptionalDependencies() {
863 for (DependencyContext d : m_dependencies) {
864 if (! d.isRequired()) {
865 for (Event e : m_dependencyEvents.get(d)) {
866 d.invokeCallback(EventType.REMOVED, e);
867 }
868 }
869 }
870 }
871
872 private void invokeRemoveInstanceBoundDependencies() {
873 for (DependencyContext d : m_dependencies) {
874 if (d.isInstanceBound()) {
875 for (Event e : m_dependencyEvents.get(d)) {
876 d.invokeCallback(EventType.REMOVED, e);
877 }
878 }
879 }
880 }
881
882 private void invoke(String name) {
883 if (name != null) {
884 // if a callback instance was specified, look for the method there, if not,
885 // ask the service for its composition instances
886 Object[] instances = m_callbackInstance != null ? new Object[] { m_callbackInstance } : getCompositionInstances();
887
888 long t1 = System.nanoTime();
889 try {
890 invokeCallbackMethod(instances, name,
891 new Class[][] {{ Component.class }, {}},
892 new Object[][] {{ this }, {}},
893 false);
894 } finally {
895 long t2 = System.nanoTime();
896 m_stopwatch.put(name, t2 - t1);
897 }
898 }
899 }
900
901 @SuppressWarnings("unchecked")
902 public <T> T getInstance() {
903 Object[] instances = getCompositionInstances();
904 return instances.length == 0 ? null : (T) instances[0];
905 }
906
907 public Object[] getInstances() {
908 return getCompositionInstances();
909 }
910
911 public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters) {
912 invokeCallbackMethod(instances, methodName, signatures, parameters, true);
913 }
914
915 public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures,
916 Object[][] parameters, boolean logIfNotFound) {
917 boolean callbackFound = false;
918 for (int i = 0; i < instances.length; i++) {
919 try {
920 InvocationUtil.invokeCallbackMethod(instances[i], methodName, signatures, parameters);
921 callbackFound |= true;
922 }
923 catch (NoSuchMethodException e) {
924 // if the method does not exist, ignore it
925 }
926 catch (InvocationTargetException e) {
927 // the method itself threw an exception, log that
928 m_logger.log(Logger.LOG_ERROR, "Invocation of '" + methodName + "' failed.", e.getCause());
929 }
930 catch (Throwable e) {
931 m_logger.log(Logger.LOG_ERROR, "Could not invoke '" + methodName + "'.", e);
932 }
933 }
934
935 // If the callback is not found, we don't log if the method is on an AbstractDecorator.
936 // (Aspect or Adapter are not interested in user dependency callbacks)
937 if (logIfNotFound && ! callbackFound && ! (getInstance() instanceof AbstractDecorator)) {
938 if (m_logger == null) {
939 System.out.println("Callback \"" + methodName + "\" not found on componnent instances "
940 + Arrays.toString(getInstances()));
941 } else {
942 m_logger.log(LogService.LOG_ERROR, "Callback \"" + methodName + "\" callback not found on componnent instances "
943 + Arrays.toString(getInstances()));
944 }
945
946 }
947 }
948
949 private Object createInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
950 Constructor<?> constructor = clazz.getConstructor(VOID);
951 constructor.setAccessible(true);
952 return constructor.newInstance();
953 }
954
955 private void notifyListeners(ComponentState state) {
956 for (ComponentStateListener l : m_listeners) {
957 l.changed(this, state);
958 }
959 }
960
961 @Override
962 public boolean isAvailable() {
963 return m_state == TRACKING_OPTIONAL;
964 }
965
966 @Override
967 public boolean isActive() {
968 return m_active.get();
969 }
970
971 @Override
972 public Component add(final ComponentStateListener l) {
973 m_listeners.add(l);
974 return this;
975 }
976
977 @Override
978 public Component remove(ComponentStateListener l) {
979 m_listeners.remove(l);
980 return this;
981 }
982
983 @SuppressWarnings("unchecked")
984 @Override
985 public List<DependencyContext> getDependencies() {
986 return (List<DependencyContext>) m_dependencies.clone();
987 }
988
989 @Override
990 public Component setImplementation(Object implementation) {
991 m_componentDefinition = implementation;
992 return this;
993 }
994
995 private void autoConfigureImplementation(Class<?> clazz, Object instance) {
996 if (((Boolean) m_autoConfig.get(clazz)).booleanValue()) {
997 configureImplementation(clazz, instance, (String) m_autoConfigInstance.get(clazz));
998 }
999 }
1000
1001 /**
1002 * Configure a field in the service implementation. The service implementation
1003 * is searched for fields that have the same type as the class that was specified
1004 * and for each of these fields, the specified instance is filled in.
1005 *
1006 * @param clazz the class to search for
1007 * @param instance the object to fill in the implementation class(es) field
1008 * @param instanceName the name of the instance to fill in, or <code>null</code> if not used
1009 */
1010 private void configureImplementation(Class<?> clazz, Object instance, String fieldName) {
1011 Object[] targets = getInstances();
1012 if (! FieldUtil.injectField(targets, fieldName, clazz, instance, m_logger) && fieldName != null) {
1013 // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
1014 // if field has not been injected.
1015 if (! (getInstance() instanceof AbstractDecorator)) {
1016 m_logger.log(Logger.LOG_ERROR, "Could not inject " + instance + " to field \"" + fieldName
1017 + "\" at any of the following component instances: " + Arrays.toString(targets));
1018 }
1019 }
1020 }
1021
1022 private void configureImplementation(Class<?> clazz, DependencyContext dc, String fieldName) {
1023 Object[] targets = getInstances();
1024 if (! FieldUtil.injectDependencyField(targets, fieldName, clazz, dc, m_logger) && fieldName != null) {
1025 // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
1026 // if field has not been injected.
1027 if (! (getInstance() instanceof AbstractDecorator)) {
1028 m_logger.log(Logger.LOG_ERROR, "Could not inject dependency " + clazz.getName() + " to field \""
1029 + fieldName + "\" at any of the following component instances: " + Arrays.toString(targets));
1030 }
1031 }
1032 }
1033
1034 /**
1035 * Update the component instances.
1036 *
1037 * @param clazz the class of the dependency service to inject in the component instances
1038 * @param dc the dependency context for the updating dependency service
1039 * @param fieldName the component instances fieldname to fill in with the updated dependency service
1040 * @param event the event holding the updating service (service + properties)
1041 * @param update true if dependency service properties are updating, false if not. If false, it means
1042 * that a dependency service is being added or removed. (see the "add" flag).
1043 * @param add true if the dependency service has been added, false if it has been removed. This flag is
1044 * ignored if the "update" flag is true (because the dependency properties are just being updated).
1045 */
1046 private void updateImplementation(Class<?> clazz, DependencyContext dc, String fieldName, Event event, boolean update,
1047 boolean add)
1048 {
1049 Object[] targets = getInstances();
1050 FieldUtil.updateDependencyField(targets, fieldName, update, add, clazz, event, dc, m_logger);
1051 }
1052
1053 @Override
1054 public ServiceRegistration getServiceRegistration() {
1055 return m_registration;
1056 }
1057
1058 @SuppressWarnings("unchecked")
1059 @Override
1060 public <K,V> Dictionary<K, V> getServiceProperties() {
1061 if (m_serviceProperties != null) {
1062 // Applied patch from FELIX-4304
1063 Hashtable<Object, Object> serviceProperties = new Hashtable<>();
1064 addTo(serviceProperties, m_serviceProperties);
1065 return (Dictionary<K, V>) serviceProperties;
1066 }
1067 return null;
1068 }
1069
1070 @Override
1071 @SuppressWarnings("unchecked")
1072 public Component setServiceProperties(final Dictionary<?, ?> serviceProperties) {
1073 getExecutor().execute(new Runnable() {
1074 @Override
1075 public void run() {
1076 Dictionary<Object, Object> properties = null;
1077 m_serviceProperties = (Dictionary<Object, Object>) serviceProperties;
1078 if ((m_registration != null) && (m_serviceName != null)) {
1079 properties = calculateServiceProperties();
1080 m_registration.setProperties(properties);
1081 }
1082 }
1083 });
1084 return this;
1085 }
1086
1087 public Component setCallbacks(String init, String start, String stop, String destroy) {
1088 ensureNotActive();
1089 m_callbackInit = init;
1090 m_callbackStart = start;
1091 m_callbackStop = stop;
1092 m_callbackDestroy = destroy;
1093 return this;
1094 }
1095
1096 public Component setCallbacks(Object instance, String init, String start, String stop, String destroy) {
1097 ensureNotActive();
1098 m_callbackInstance = instance;
1099 m_callbackInit = init;
1100 m_callbackStart = start;
1101 m_callbackStop = stop;
1102 m_callbackDestroy = destroy;
1103 return this;
1104 }
1105
1106 @Override
1107 public Component setFactory(Object factory, String createMethod) {
1108 ensureNotActive();
1109 m_instanceFactory = factory;
1110 m_instanceFactoryCreateMethod = createMethod;
1111 return this;
1112 }
1113
1114 @Override
1115 public Component setFactory(String createMethod) {
1116 return setFactory(null, createMethod);
1117 }
1118
1119 @Override
1120 public Component setComposition(Object instance, String getMethod) {
1121 ensureNotActive();
1122 m_compositionManager = instance;
1123 m_compositionManagerGetMethod = getMethod;
1124 return this;
1125 }
1126
1127 @Override
1128 public Component setComposition(String getMethod) {
1129 return setComposition(null, getMethod);
1130 }
1131
1132 private Object[] getCompositionInstances() {
1133 Object[] instances = null;
1134 if (m_compositionManagerGetMethod != null) {
1135 if (m_compositionManager != null) {
1136 m_compositionManagerInstance = m_compositionManager;
1137 }
1138 else {
1139 m_compositionManagerInstance = m_componentInstance;
1140 }
1141 if (m_compositionManagerInstance != null) {
1142 try {
1143 instances = (Object[]) InvocationUtil.invokeMethod(m_compositionManagerInstance, m_compositionManagerInstance.getClass(), m_compositionManagerGetMethod, new Class[][] {{}}, new Object[][] {{}}, false);
1144 }
1145 catch (Exception e) {
1146 m_logger.log(Logger.LOG_ERROR, "Could not obtain instances from the composition manager.", e);
1147 instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
1148 }
1149 }
1150 }
1151 else {
1152 instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
1153 }
1154 return instances;
1155 }
1156
1157 @Override
1158 public DependencyManager getDependencyManager() {
1159 return m_manager;
1160 }
1161
1162 public ComponentDependencyDeclaration[] getComponentDependencies() {
1163 List<DependencyContext> deps = getDependencies();
1164 if (deps != null) {
1165 ComponentDependencyDeclaration[] result = new ComponentDependencyDeclaration[deps.size()];
1166 for (int i = 0; i < result.length; i++) {
1167 DependencyContext dep = (DependencyContext) deps.get(i);
1168 if (dep instanceof ComponentDependencyDeclaration) {
1169 result[i] = (ComponentDependencyDeclaration) dep;
1170 }
1171 else {
1172 result[i] = new SCDImpl(dep.toString(), (dep.isAvailable() ? 1 : 0) + (dep.isRequired() ? 2 : 0), dep.getClass().getName());
1173 }
1174 }
1175 return result;
1176 }
1177 return null;
1178 }
1179
1180 public String getName() {
1181 StringBuffer sb = new StringBuffer();
1182 Object serviceName = m_serviceName;
1183 if (serviceName instanceof String[]) {
1184 String[] names = (String[]) serviceName;
1185 for (int i = 0; i < names.length; i++) {
1186 if (i > 0) {
1187 sb.append(", ");
1188 }
1189 sb.append(names[i]);
1190 }
1191 appendProperties(sb);
1192 } else if (serviceName instanceof String) {
1193 sb.append(serviceName.toString());
1194 appendProperties(sb);
1195 } else {
1196 Object implementation = m_componentDefinition;
1197 if (implementation != null) {
1198 if (implementation instanceof Class) {
1199 sb.append(((Class<?>) implementation).getName());
1200 } else {
1201 // If the implementation instance does not override "toString", just display
1202 // the class name, else display the component using its toString method
1203 try {
1204 Method m = implementation.getClass().getMethod("toString", new Class[0]);
1205 if (m.getDeclaringClass().equals(Object.class)) {
1206 sb.append(implementation.getClass().getName());
1207 } else {
1208 sb.append(implementation.toString());
1209 }
1210 } catch (java.lang.NoSuchMethodException e) {
1211 // Just display the class name
1212 sb.append(implementation.getClass().getName());
1213 }
1214 }
1215 } else {
1216 sb.append(super.toString());
1217 }
1218 }
1219 return sb.toString();
1220 }
1221
1222 private void appendProperties(StringBuffer result) {
1223 Dictionary<Object, Object> properties = calculateServiceProperties();
1224 if (properties != null) {
1225 result.append("(");
1226 Enumeration<?> enumeration = properties.keys();
1227 while (enumeration.hasMoreElements()) {
1228 Object key = enumeration.nextElement();
1229 result.append(key.toString());
1230 result.append('=');
1231 Object value = properties.get(key);
1232 if (value instanceof String[]) {
1233 String[] values = (String[]) value;
1234 result.append('{');
1235 for (int i = 0; i < values.length; i++) {
1236 if (i > 0) {
1237 result.append(',');
1238 }
1239 result.append(values[i].toString());
1240 }
1241 result.append('}');
1242 }
1243 else {
1244 result.append(value.toString());
1245 }
1246 if (enumeration.hasMoreElements()) {
1247 result.append(',');
1248 }
1249 }
1250 result.append(")");
1251 }
1252 }
1253
1254 @Override
1255 public BundleContext getBundleContext() {
1256 return m_context;
1257 }
1258
1259 @Override
1260 public Bundle getBundle() {
1261 return m_bundle;
1262 }
1263
1264 public long getId() {
1265 return m_id;
1266 }
1267
1268 public String getClassName() {
1269 Object serviceInstance = m_componentInstance;
1270 if (serviceInstance != null) {
1271 return serviceInstance.getClass().getName();
1272 }
1273
1274 Object implementation = m_componentDefinition;
1275 if (implementation != null) {
1276 if (implementation instanceof Class) {
1277 return ((Class<?>) implementation).getName();
1278 }
1279 return implementation.getClass().getName();
1280 }
1281
1282 Object instanceFactory = m_instanceFactory;
1283 if (instanceFactory != null) {
1284 return instanceFactory.getClass().getName();
1285 } else {
1286 // unexpected.
1287 return ComponentImpl.class.getName();
1288 }
1289 }
1290
1291 public String[] getServices() {
1292 if (m_serviceName instanceof String[]) {
1293 return (String[]) m_serviceName;
1294 } else if (m_serviceName instanceof String) {
1295 return new String[] { (String) m_serviceName };
1296 } else {
1297 return null;
1298 }
1299 }
1300
1301 public int getState() {
1302 return (isAvailable() ? ComponentDeclaration.STATE_REGISTERED : ComponentDeclaration.STATE_UNREGISTERED);
1303 }
1304
1305 public void ensureNotActive() {
1306 if (m_active.get()) {
1307 throw new IllegalStateException("Can't modify an already started component.");
1308 }
1309 }
1310
1311 public ComponentDeclaration getComponentDeclaration() {
1312 return this;
1313 }
1314
1315 @Override
1316 public String toString() {
1317 if (m_logger.getDebugKey() != null) {
1318 return m_logger.getDebugKey();
1319 }
1320 return getClassName();
1321 }
1322
1323 @Override
1324 public void setThreadPool(Executor threadPool) {
1325 ensureNotActive();
1326 m_executor = new DispatchExecutor(threadPool, m_logger);
1327 }
1328
1329 @Override
1330 public Logger getLogger() {
1331 return m_logger;
1332 }
1333
1334 @Override
1335 public Map<String, Long> getCallbacksTime() {
1336 return m_stopwatch;
1337 }
1338
1339 private Executor getExecutor() {
1340 return m_executor;
1341 }
1342}