blob: 9652e8f192ba6e9f4c0ba025f82ed2a2bcbde8c6 [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 Ropc40d93f2015-05-04 20:25:57 +0000346 // In the following switch block, we sometimes only recalculate state changes
Pierre De Ropd2012be2015-04-28 12:17:36 +0000347 // 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).
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000349 // And in this case, depending on the state, we don't recalculate state changes now. We'll do it
Pierre De Ropd2012be2015-04-28 12:17:36 +0000350 // 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:
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000354 // 1- optimization: it is preferable to recalculate state changes once we know about all currently
355 // available dependency services (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
357 // the component start method callback (this will be extremely useful when porting the Felix SCR
358 // on top of DM4).
Pierre De Rop3a00a212015-03-01 09:27:46 +0000359
Pierre De Ropd2012be2015-04-28 12:17:36 +0000360 switch (m_state) {
361 case WAITING_FOR_REQUIRED:
362 if (dc.isStarted() && dc.isRequired()) {
363 // if dependency is starting, we'll handle change after the tracker has returned (see startDependencies method).
364 handleChange();
Pierre De Rop3a00a212015-03-01 09:27:46 +0000365 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000366 break;
367 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
368 if (!dc.isInstanceBound()) {
369 if (dc.isRequired()) {
370 dc.invokeCallback(EventType.ADDED, e);
371 }
372 updateInstance(dc, e, false, true);
373 } else {
374 if (dc.isStarted() && dc.isRequired()) {
375 // if dependency is starting, we'll handle change after the tracker has returned (see startDependencies method).
376 handleChange();
377 }
378 }
379 break;
380 case TRACKING_OPTIONAL:
381 dc.invokeCallback(EventType.ADDED, e);
382 updateInstance(dc, e, false, true);
383 break;
384 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +0000385 }
386 }
387
388 private void handleChanged(final DependencyContext dc, final Event e) {
389 if (! m_isStarted) {
390 return;
391 }
392 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
393 dependencyEvents.remove(e);
394 dependencyEvents.add(e);
395
Pierre De Ropd2012be2015-04-28 12:17:36 +0000396 switch (m_state) {
397 case TRACKING_OPTIONAL:
398 dc.invokeCallback(EventType.CHANGED, e);
399 updateInstance(dc, e, true, false);
400 break;
401
402 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
403 if (!dc.isInstanceBound()) {
Pierre De Rop3a00a212015-03-01 09:27:46 +0000404 dc.invokeCallback(EventType.CHANGED, e);
405 updateInstance(dc, e, true, false);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000406 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000407 break;
408 default:
409 // noop
Pierre De Rop3a00a212015-03-01 09:27:46 +0000410 }
411 }
412
413 private void handleRemoved(DependencyContext dc, Event e) {
414 if (! m_isStarted) {
415 return;
416 }
417 // Check if the dependency is still available.
418 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
419 int size = dependencyEvents.size();
420 if (dependencyEvents.contains(e)) {
421 size--; // the dependency is currently registered and is about to be removed.
422 }
423 dc.setAvailable(size > 0);
424
425 // If the dependency is now unavailable, we have to recalculate state change. This will result in invoking the
426 // "removed" callback with the removed dependency (which we have not yet removed from our dependency events list.).
427 // But we don't recalculate the state if the dependency is not started (if not started, it means that it is currently starting,
428 // and the tracker is detecting a removed service).
429 if (size == 0 && dc.isStarted()) {
430 handleChange();
431 }
432
433 // Now, really remove the dependency event.
434 dependencyEvents.remove(e);
435
Pierre De Ropd2012be2015-04-28 12:17:36 +0000436 // Depending on the state, we possible have to invoke the callbacks and update the component instance.
437 switch (m_state) {
438 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
439 if (!dc.isInstanceBound()) {
440 if (dc.isRequired()) {
441 dc.invokeCallback(EventType.REMOVED, e);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000442 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000443 updateInstance(dc, e, false, false);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000444 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000445 break;
446 case TRACKING_OPTIONAL:
447 dc.invokeCallback(EventType.REMOVED, e);
448 updateInstance(dc, e, false, false);
449 break;
450 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +0000451 }
452 }
453
454 private void handleSwapped(DependencyContext dc, Event oldEvent, Event newEvent) {
455 if (! m_isStarted) {
456 return;
457 }
458 Set<Event> dependencyEvents = m_dependencyEvents.get(dc);
459 dependencyEvents.remove(oldEvent);
460 dependencyEvents.add(newEvent);
461
Pierre De Ropd2012be2015-04-28 12:17:36 +0000462 // Depending on the state, we possible have to invoke the callbacks and update the component instance.
463 switch (m_state) {
464 case WAITING_FOR_REQUIRED:
465 // No need to swap, we don't have yet injected anything
466 break;
467 case INSTANTIATED_AND_WAITING_FOR_REQUIRED:
468 // Only swap *non* instance-bound dependencies
469 if (!dc.isInstanceBound()) {
470 if (dc.isRequired()) {
471 dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000472 }
Pierre De Rop3a00a212015-03-01 09:27:46 +0000473 }
Pierre De Ropd2012be2015-04-28 12:17:36 +0000474 break;
475 case TRACKING_OPTIONAL:
476 dc.invokeCallback(EventType.SWAPPED, oldEvent, newEvent);
477 break;
478 default:
Pierre De Rop3a00a212015-03-01 09:27:46 +0000479 }
480 }
481
482 private void handleChange() {
483 m_logger.debug("handleChanged");
484 try {
485 ComponentState oldState;
486 ComponentState newState;
487 do {
488 oldState = m_state;
489 newState = calculateNewState(oldState);
490 m_logger.debug("%s -> %s", oldState, newState);
491 m_state = newState;
492 } while (performTransition(oldState, newState));
493 } finally {
494 m_logger.debug("end handling change.");
495 }
496 }
497
498 /** Based on the current state, calculate the new state. */
499 private ComponentState calculateNewState(ComponentState currentState) {
500 if (currentState == INACTIVE) {
501 if (m_isStarted) {
502 return WAITING_FOR_REQUIRED;
503 }
504 }
505 if (currentState == WAITING_FOR_REQUIRED) {
506 if (!m_isStarted) {
507 return INACTIVE;
508 }
509 if (allRequiredAvailable()) {
510 return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
511 }
512 }
513 if (currentState == INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
514 if (m_isStarted && allRequiredAvailable()) {
515 if (allInstanceBoundAvailable()) {
516 return TRACKING_OPTIONAL;
517 }
518 return currentState;
519 }
520 return WAITING_FOR_REQUIRED;
521 }
522 if (currentState == TRACKING_OPTIONAL) {
523 if (m_isStarted && allRequiredAvailable() && allInstanceBoundAvailable()) {
524 return currentState;
525 }
526 return INSTANTIATED_AND_WAITING_FOR_REQUIRED;
527 }
528 return currentState;
529 }
530
531 /** Perform all the actions associated with state transitions. Returns true if a transition was performed. */
532 private boolean performTransition(ComponentState oldState, ComponentState newState) {
533// System.out.println("transition from " + oldState + " to " + newState);
534 if (oldState == ComponentState.INACTIVE && newState == ComponentState.WAITING_FOR_REQUIRED) {
535 startDependencies(m_dependencies);
536 notifyListeners(newState);
537 return true;
538 }
539 if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
540 instantiateComponent();
541 invokeAutoConfigDependencies();
542 invokeAddRequiredDependencies();
543 ComponentState stateBeforeCallingInit = m_state;
544 invoke(m_callbackInit);
545 if (stateBeforeCallingInit == m_state) {
546 notifyListeners(newState); // init did not change current state, we can notify about this new state
547 }
548 return true;
549 }
550 if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.TRACKING_OPTIONAL) {
551 invokeAutoConfigInstanceBoundDependencies();
552 invokeAddRequiredInstanceBoundDependencies();
553 invoke(m_callbackStart);
554 invokeAddOptionalDependencies();
555 registerService();
556 notifyListeners(newState);
557 return true;
558 }
559 if (oldState == ComponentState.TRACKING_OPTIONAL && newState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
560 unregisterService();
561 invokeRemoveOptionalDependencies();
562 invoke(m_callbackStop);
563 invokeRemoveInstanceBoundDependencies();
564 notifyListeners(newState);
565 return true;
566 }
567 if (oldState == ComponentState.INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == ComponentState.WAITING_FOR_REQUIRED) {
568 invoke(m_callbackDestroy);
569 invokeRemoveRequiredDependencies();
570 notifyListeners(newState);
571 if (! someDependenciesNeedInstance()) {
572 destroyComponent();
573 }
574 return true;
575 }
576 if (oldState == ComponentState.WAITING_FOR_REQUIRED && newState == ComponentState.INACTIVE) {
577 stopDependencies();
578 destroyComponent();
579 notifyListeners(newState);
580 return true;
581 }
582 return false;
583 }
584
585 private boolean allRequiredAvailable() {
586 boolean available = true;
587 for (DependencyContext d : m_dependencies) {
588 if (d.isRequired() && !d.isInstanceBound()) {
589 if (!d.isAvailable()) {
590 available = false;
591 break;
592 }
593 }
594 }
595 return available;
596 }
597
598 private boolean allInstanceBoundAvailable() {
599 boolean available = true;
600 for (DependencyContext d : m_dependencies) {
601 if (d.isRequired() && d.isInstanceBound()) {
602 if (!d.isAvailable()) {
603 available = false;
604 break;
605 }
606 }
607 }
608 return available;
609 }
610
611 private boolean someDependenciesNeedInstance() {
612 for (DependencyContext d : m_dependencies) {
613 if (d.needsInstance()) {
614 return true;
615 }
616 }
617 return false;
618 }
619
620 /**
621 * Updates the component instance(s).
622 * @param dc the dependency context for the updating dependency service
623 * @param event the event holding the updating service (service + properties)
624 * @param update true if dependency service properties are updating, false if not. If false, it means
625 * that a dependency service is being added or removed. (see the "add" flag).
626 * @param add true if the dependency service has been added, false if it has been removed. This flag is
627 * ignored if the "update" flag is true (because the dependency properties are just being updated).
628 */
629 private void updateInstance(DependencyContext dc, Event event, boolean update, boolean add) {
630 if (dc.isAutoConfig()) {
631 updateImplementation(dc.getAutoConfigType(), dc, dc.getAutoConfigName(), event, update, add);
632 }
633 if (dc.isPropagated() && m_registration != null) {
634 m_registration.setProperties(calculateServiceProperties());
635 }
636 }
637
638 private void startDependencies(List<DependencyContext> dependencies) {
639 // Start first optional dependencies first.
640 m_logger.debug("startDependencies.");
641 List<DependencyContext> requiredDeps = new ArrayList<>();
642 for (DependencyContext d : dependencies) {
643 if (d.isRequired()) {
644 requiredDeps.add(d);
645 continue;
646 }
647 if (d.needsInstance()) {
648 instantiateComponent();
649 }
650 d.start();
651 }
652 // now, start required dependencies.
653 for (DependencyContext d : requiredDeps) {
654 if (d.needsInstance()) {
655 instantiateComponent();
656 }
657 d.start();
658 }
659 // The started dependencies has probably called our handleAdded method: we now have to run our state machine.
660 handleChange();
661 }
662
663 private void stopDependencies() {
664 for (DependencyContext d : m_dependencies) {
665 d.stop();
666 }
667 }
668
669 private void registerService() {
670 if (m_context != null && m_serviceName != null) {
671 ServiceRegistrationImpl wrapper = new ServiceRegistrationImpl();
672 m_registration = wrapper;
673 autoConfigureImplementation(ServiceRegistration.class, m_registration);
674
675 // service name can either be a string or an array of strings
676 ServiceRegistration registration;
677
678 // determine service properties
679 Dictionary<?,?> properties = calculateServiceProperties();
680
681 // register the service
682 try {
683 if (m_serviceName instanceof String) {
684 registration = m_context.registerService((String) m_serviceName, m_componentInstance, properties);
685 }
686 else {
687 registration = m_context.registerService((String[]) m_serviceName, m_componentInstance, properties);
688 }
689 wrapper.setServiceRegistration(registration);
690 }
691 catch (IllegalArgumentException iae) {
692 m_logger.log(Logger.LOG_ERROR, "Could not register service " + m_componentInstance, iae);
693 // set the registration to an illegal state object, which will make all invocations on this
694 // wrapper fail with an ISE (which also occurs when the SR becomes invalid)
695 wrapper.setIllegalState();
696 }
697 }
698 }
699
700 private void unregisterService() {
701 if (m_serviceName != null && m_registration != null) {
702 try {
703 if (m_bundle != null && m_bundle.getState() == Bundle.ACTIVE) {
704 m_registration.unregister();
705 }
706 } catch (IllegalStateException e) { /* Should we really log this ? */}
707 autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
708 m_registration = null;
709 }
710 }
711
712 private Dictionary<Object, Object> calculateServiceProperties() {
713 Dictionary<Object, Object> properties = new Hashtable<>();
714 for (int i = 0; i < m_dependencies.size(); i++) {
715 DependencyContext d = (DependencyContext) m_dependencies.get(i);
716 if (d.isPropagated() && d.isAvailable()) {
717 Dictionary<Object, Object> dict = d.getProperties();
718 addTo(properties, dict);
719 }
720 }
721 // our service properties must not be overriden by propagated dependency properties, so we add our service
722 // properties after having added propagated dependency properties.
723 addTo(properties, m_serviceProperties);
724 if (properties.size() == 0) {
725 properties = null;
726 }
727 return properties;
728 }
729
730 private void addTo(Dictionary<Object, Object> properties, Dictionary<Object, Object> additional) {
731 if (properties == null) {
732 throw new IllegalArgumentException("Dictionary to add to cannot be null.");
733 }
734 if (additional != null) {
735 Enumeration<Object> e = additional.keys();
736 while (e.hasMoreElements()) {
737 Object key = e.nextElement();
738 properties.put(key, additional.get(key));
739 }
740 }
741 }
742
Pierre De Ropd2012be2015-04-28 12:17:36 +0000743 void instantiateComponent() {
Pierre De Rop3a00a212015-03-01 09:27:46 +0000744 m_logger.debug("instantiating component.");
745
746 // TODO add more complex factory instantiations of one or more components in a composition here
747 if (m_componentInstance == null) {
748 if (m_componentDefinition instanceof Class) {
749 try {
750 m_componentInstance = createInstance((Class<?>) m_componentDefinition);
751 }
752 catch (Exception e) {
753 m_logger.log(Logger.LOG_ERROR, "Could not instantiate class " + m_componentDefinition, e);
754 }
755 }
756 else {
757 if (m_instanceFactoryCreateMethod != null) {
758 Object factory = null;
759 if (m_instanceFactory != null) {
760 if (m_instanceFactory instanceof Class) {
761 try {
762 factory = createInstance((Class<?>) m_instanceFactory);
763 }
764 catch (Exception e) {
765 m_logger.log(Logger.LOG_ERROR, "Could not create factory instance of class " + m_instanceFactory + ".", e);
766 }
767 }
768 else {
769 factory = m_instanceFactory;
770 }
771 }
772 else {
773 // TODO review if we want to try to default to something if not specified
774 // for now the JavaDoc of setFactory(method) reflects the fact that we need
775 // to review it
776 }
777 if (factory == null) {
778 m_logger.log(Logger.LOG_ERROR, "Factory cannot be null.");
779 }
780 else {
781 try {
Pierre De Ropc40d93f2015-05-04 20:25:57 +0000782 m_componentInstance = InvocationUtil.invokeMethod(factory,
783 factory.getClass(), m_instanceFactoryCreateMethod,
784 new Class[][] {{}, {Component.class}}, new Object[][] {{}, {this}}, false);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000785 }
786 catch (Exception e) {
787 m_logger.log(Logger.LOG_ERROR, "Could not create service instance using factory " + factory + " method " + m_instanceFactoryCreateMethod + ".", e);
788 }
789 }
790 }
791 }
792
793 if (m_componentInstance == null) {
794 m_componentInstance = m_componentDefinition;
795 }
796
797 // configure the bundle context
798 autoConfigureImplementation(BundleContext.class, m_context);
799 autoConfigureImplementation(ServiceRegistration.class, NULL_REGISTRATION);
800 autoConfigureImplementation(DependencyManager.class, m_manager);
801 autoConfigureImplementation(Component.class, this);
802 }
803 }
804
805 private void destroyComponent() {
806 m_componentInstance = null;
807 }
808
809 private void invokeAddRequiredDependencies() {
810 for (DependencyContext d : m_dependencies) {
811 if (d.isRequired() && !d.isInstanceBound()) {
812 for (Event e : m_dependencyEvents.get(d)) {
813 d.invokeCallback(EventType.ADDED, e);
814 }
815 }
816 }
817 }
818
819 private void invokeAutoConfigDependencies() {
820 for (DependencyContext d : m_dependencies) {
821 if (d.isAutoConfig() && !d.isInstanceBound()) {
822 configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
823 }
824 }
825 }
826
827 private void invokeAutoConfigInstanceBoundDependencies() {
828 for (DependencyContext d : m_dependencies) {
829 if (d.isAutoConfig() && d.isInstanceBound()) {
830 configureImplementation(d.getAutoConfigType(), d, d.getAutoConfigName());
831 }
832 }
833 }
834
835 private void invokeAddRequiredInstanceBoundDependencies() {
836 for (DependencyContext d : m_dependencies) {
837 if (d.isRequired() && d.isInstanceBound()) {
838 for (Event e : m_dependencyEvents.get(d)) {
839 d.invokeCallback(EventType.ADDED, e);
840 }
841 }
842 }
843 }
844
845 private void invokeAddOptionalDependencies() {
846 for (DependencyContext d : m_dependencies) {
847 if (! d.isRequired()) {
848 for (Event e : m_dependencyEvents.get(d)) {
849 d.invokeCallback(EventType.ADDED, e);
850 }
851 }
852 }
853 }
854
855 private void invokeRemoveRequiredDependencies() {
856 for (DependencyContext d : m_dependencies) {
857 if (!d.isInstanceBound() && d.isRequired()) {
858 for (Event e : m_dependencyEvents.get(d)) {
859 d.invokeCallback(EventType.REMOVED, e);
860 }
861 }
862 }
863 }
864
865 private void invokeRemoveOptionalDependencies() {
866 for (DependencyContext d : m_dependencies) {
867 if (! d.isRequired()) {
868 for (Event e : m_dependencyEvents.get(d)) {
869 d.invokeCallback(EventType.REMOVED, e);
870 }
871 }
872 }
873 }
874
875 private void invokeRemoveInstanceBoundDependencies() {
876 for (DependencyContext d : m_dependencies) {
877 if (d.isInstanceBound()) {
878 for (Event e : m_dependencyEvents.get(d)) {
879 d.invokeCallback(EventType.REMOVED, e);
880 }
881 }
882 }
883 }
884
885 private void invoke(String name) {
886 if (name != null) {
887 // if a callback instance was specified, look for the method there, if not,
888 // ask the service for its composition instances
889 Object[] instances = m_callbackInstance != null ? new Object[] { m_callbackInstance } : getCompositionInstances();
890
891 long t1 = System.nanoTime();
892 try {
893 invokeCallbackMethod(instances, name,
894 new Class[][] {{ Component.class }, {}},
895 new Object[][] {{ this }, {}},
896 false);
897 } finally {
898 long t2 = System.nanoTime();
899 m_stopwatch.put(name, t2 - t1);
900 }
901 }
902 }
903
904 @SuppressWarnings("unchecked")
905 public <T> T getInstance() {
906 Object[] instances = getCompositionInstances();
907 return instances.length == 0 ? null : (T) instances[0];
908 }
909
910 public Object[] getInstances() {
911 return getCompositionInstances();
912 }
913
914 public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures, Object[][] parameters) {
915 invokeCallbackMethod(instances, methodName, signatures, parameters, true);
916 }
917
918 public void invokeCallbackMethod(Object[] instances, String methodName, Class<?>[][] signatures,
919 Object[][] parameters, boolean logIfNotFound) {
920 boolean callbackFound = false;
921 for (int i = 0; i < instances.length; i++) {
922 try {
923 InvocationUtil.invokeCallbackMethod(instances[i], methodName, signatures, parameters);
924 callbackFound |= true;
925 }
926 catch (NoSuchMethodException e) {
927 // if the method does not exist, ignore it
928 }
929 catch (InvocationTargetException e) {
930 // the method itself threw an exception, log that
931 m_logger.log(Logger.LOG_ERROR, "Invocation of '" + methodName + "' failed.", e.getCause());
932 }
933 catch (Throwable e) {
934 m_logger.log(Logger.LOG_ERROR, "Could not invoke '" + methodName + "'.", e);
935 }
936 }
937
938 // If the callback is not found, we don't log if the method is on an AbstractDecorator.
939 // (Aspect or Adapter are not interested in user dependency callbacks)
940 if (logIfNotFound && ! callbackFound && ! (getInstance() instanceof AbstractDecorator)) {
941 if (m_logger == null) {
942 System.out.println("Callback \"" + methodName + "\" not found on componnent instances "
943 + Arrays.toString(getInstances()));
944 } else {
945 m_logger.log(LogService.LOG_ERROR, "Callback \"" + methodName + "\" callback not found on componnent instances "
946 + Arrays.toString(getInstances()));
947 }
948
949 }
950 }
951
952 private Object createInstance(Class<?> clazz) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
953 Constructor<?> constructor = clazz.getConstructor(VOID);
954 constructor.setAccessible(true);
955 return constructor.newInstance();
956 }
957
958 private void notifyListeners(ComponentState state) {
959 for (ComponentStateListener l : m_listeners) {
960 l.changed(this, state);
961 }
962 }
963
964 @Override
965 public boolean isAvailable() {
966 return m_state == TRACKING_OPTIONAL;
967 }
968
969 @Override
970 public boolean isActive() {
971 return m_active.get();
972 }
973
974 @Override
975 public Component add(final ComponentStateListener l) {
976 m_listeners.add(l);
977 return this;
978 }
979
980 @Override
981 public Component remove(ComponentStateListener l) {
982 m_listeners.remove(l);
983 return this;
984 }
985
986 @SuppressWarnings("unchecked")
987 @Override
988 public List<DependencyContext> getDependencies() {
989 return (List<DependencyContext>) m_dependencies.clone();
990 }
991
992 @Override
993 public Component setImplementation(Object implementation) {
994 m_componentDefinition = implementation;
995 return this;
996 }
997
998 private void autoConfigureImplementation(Class<?> clazz, Object instance) {
999 if (((Boolean) m_autoConfig.get(clazz)).booleanValue()) {
1000 configureImplementation(clazz, instance, (String) m_autoConfigInstance.get(clazz));
1001 }
1002 }
1003
1004 /**
1005 * Configure a field in the service implementation. The service implementation
1006 * is searched for fields that have the same type as the class that was specified
1007 * and for each of these fields, the specified instance is filled in.
1008 *
1009 * @param clazz the class to search for
1010 * @param instance the object to fill in the implementation class(es) field
1011 * @param instanceName the name of the instance to fill in, or <code>null</code> if not used
1012 */
1013 private void configureImplementation(Class<?> clazz, Object instance, String fieldName) {
1014 Object[] targets = getInstances();
1015 if (! FieldUtil.injectField(targets, fieldName, clazz, instance, m_logger) && fieldName != null) {
1016 // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
1017 // if field has not been injected.
1018 if (! (getInstance() instanceof AbstractDecorator)) {
1019 m_logger.log(Logger.LOG_ERROR, "Could not inject " + instance + " to field \"" + fieldName
1020 + "\" at any of the following component instances: " + Arrays.toString(targets));
1021 }
1022 }
1023 }
1024
1025 private void configureImplementation(Class<?> clazz, DependencyContext dc, String fieldName) {
1026 Object[] targets = getInstances();
1027 if (! FieldUtil.injectDependencyField(targets, fieldName, clazz, dc, m_logger) && fieldName != null) {
1028 // If the target is an abstract decorator (i.e: an adapter, or an aspect), we must not log warnings
1029 // if field has not been injected.
1030 if (! (getInstance() instanceof AbstractDecorator)) {
1031 m_logger.log(Logger.LOG_ERROR, "Could not inject dependency " + clazz.getName() + " to field \""
1032 + fieldName + "\" at any of the following component instances: " + Arrays.toString(targets));
1033 }
1034 }
1035 }
1036
1037 /**
1038 * Update the component instances.
1039 *
1040 * @param clazz the class of the dependency service to inject in the component instances
1041 * @param dc the dependency context for the updating dependency service
1042 * @param fieldName the component instances fieldname to fill in with the updated dependency service
1043 * @param event the event holding the updating service (service + properties)
1044 * @param update true if dependency service properties are updating, false if not. If false, it means
1045 * that a dependency service is being added or removed. (see the "add" flag).
1046 * @param add true if the dependency service has been added, false if it has been removed. This flag is
1047 * ignored if the "update" flag is true (because the dependency properties are just being updated).
1048 */
1049 private void updateImplementation(Class<?> clazz, DependencyContext dc, String fieldName, Event event, boolean update,
1050 boolean add)
1051 {
1052 Object[] targets = getInstances();
1053 FieldUtil.updateDependencyField(targets, fieldName, update, add, clazz, event, dc, m_logger);
1054 }
1055
1056 @Override
1057 public ServiceRegistration getServiceRegistration() {
1058 return m_registration;
1059 }
1060
1061 @SuppressWarnings("unchecked")
1062 @Override
1063 public <K,V> Dictionary<K, V> getServiceProperties() {
1064 if (m_serviceProperties != null) {
1065 // Applied patch from FELIX-4304
1066 Hashtable<Object, Object> serviceProperties = new Hashtable<>();
1067 addTo(serviceProperties, m_serviceProperties);
1068 return (Dictionary<K, V>) serviceProperties;
1069 }
1070 return null;
1071 }
1072
1073 @Override
1074 @SuppressWarnings("unchecked")
1075 public Component setServiceProperties(final Dictionary<?, ?> serviceProperties) {
1076 getExecutor().execute(new Runnable() {
1077 @Override
1078 public void run() {
1079 Dictionary<Object, Object> properties = null;
1080 m_serviceProperties = (Dictionary<Object, Object>) serviceProperties;
1081 if ((m_registration != null) && (m_serviceName != null)) {
1082 properties = calculateServiceProperties();
1083 m_registration.setProperties(properties);
1084 }
1085 }
1086 });
1087 return this;
1088 }
1089
1090 public Component setCallbacks(String init, String start, String stop, String destroy) {
1091 ensureNotActive();
1092 m_callbackInit = init;
1093 m_callbackStart = start;
1094 m_callbackStop = stop;
1095 m_callbackDestroy = destroy;
1096 return this;
1097 }
1098
1099 public Component setCallbacks(Object instance, String init, String start, String stop, String destroy) {
1100 ensureNotActive();
1101 m_callbackInstance = instance;
1102 m_callbackInit = init;
1103 m_callbackStart = start;
1104 m_callbackStop = stop;
1105 m_callbackDestroy = destroy;
1106 return this;
1107 }
1108
1109 @Override
1110 public Component setFactory(Object factory, String createMethod) {
1111 ensureNotActive();
1112 m_instanceFactory = factory;
1113 m_instanceFactoryCreateMethod = createMethod;
1114 return this;
1115 }
1116
1117 @Override
1118 public Component setFactory(String createMethod) {
1119 return setFactory(null, createMethod);
1120 }
1121
1122 @Override
1123 public Component setComposition(Object instance, String getMethod) {
1124 ensureNotActive();
1125 m_compositionManager = instance;
1126 m_compositionManagerGetMethod = getMethod;
1127 return this;
1128 }
1129
1130 @Override
1131 public Component setComposition(String getMethod) {
1132 return setComposition(null, getMethod);
1133 }
1134
1135 private Object[] getCompositionInstances() {
1136 Object[] instances = null;
1137 if (m_compositionManagerGetMethod != null) {
1138 if (m_compositionManager != null) {
1139 m_compositionManagerInstance = m_compositionManager;
1140 }
1141 else {
1142 m_compositionManagerInstance = m_componentInstance;
1143 }
1144 if (m_compositionManagerInstance != null) {
1145 try {
1146 instances = (Object[]) InvocationUtil.invokeMethod(m_compositionManagerInstance, m_compositionManagerInstance.getClass(), m_compositionManagerGetMethod, new Class[][] {{}}, new Object[][] {{}}, false);
1147 }
1148 catch (Exception e) {
1149 m_logger.log(Logger.LOG_ERROR, "Could not obtain instances from the composition manager.", e);
1150 instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
1151 }
1152 }
1153 }
1154 else {
1155 instances = m_componentInstance == null ? new Object[] {} : new Object[] { m_componentInstance };
1156 }
1157 return instances;
1158 }
1159
1160 @Override
1161 public DependencyManager getDependencyManager() {
1162 return m_manager;
1163 }
1164
1165 public ComponentDependencyDeclaration[] getComponentDependencies() {
1166 List<DependencyContext> deps = getDependencies();
1167 if (deps != null) {
1168 ComponentDependencyDeclaration[] result = new ComponentDependencyDeclaration[deps.size()];
1169 for (int i = 0; i < result.length; i++) {
1170 DependencyContext dep = (DependencyContext) deps.get(i);
1171 if (dep instanceof ComponentDependencyDeclaration) {
1172 result[i] = (ComponentDependencyDeclaration) dep;
1173 }
1174 else {
1175 result[i] = new SCDImpl(dep.toString(), (dep.isAvailable() ? 1 : 0) + (dep.isRequired() ? 2 : 0), dep.getClass().getName());
1176 }
1177 }
1178 return result;
1179 }
1180 return null;
1181 }
1182
1183 public String getName() {
1184 StringBuffer sb = new StringBuffer();
1185 Object serviceName = m_serviceName;
1186 if (serviceName instanceof String[]) {
1187 String[] names = (String[]) serviceName;
1188 for (int i = 0; i < names.length; i++) {
1189 if (i > 0) {
1190 sb.append(", ");
1191 }
1192 sb.append(names[i]);
1193 }
1194 appendProperties(sb);
1195 } else if (serviceName instanceof String) {
1196 sb.append(serviceName.toString());
1197 appendProperties(sb);
1198 } else {
1199 Object implementation = m_componentDefinition;
1200 if (implementation != null) {
1201 if (implementation instanceof Class) {
1202 sb.append(((Class<?>) implementation).getName());
1203 } else {
1204 // If the implementation instance does not override "toString", just display
1205 // the class name, else display the component using its toString method
1206 try {
1207 Method m = implementation.getClass().getMethod("toString", new Class[0]);
1208 if (m.getDeclaringClass().equals(Object.class)) {
1209 sb.append(implementation.getClass().getName());
1210 } else {
1211 sb.append(implementation.toString());
1212 }
1213 } catch (java.lang.NoSuchMethodException e) {
1214 // Just display the class name
1215 sb.append(implementation.getClass().getName());
1216 }
1217 }
1218 } else {
1219 sb.append(super.toString());
1220 }
1221 }
1222 return sb.toString();
1223 }
1224
1225 private void appendProperties(StringBuffer result) {
1226 Dictionary<Object, Object> properties = calculateServiceProperties();
1227 if (properties != null) {
1228 result.append("(");
1229 Enumeration<?> enumeration = properties.keys();
1230 while (enumeration.hasMoreElements()) {
1231 Object key = enumeration.nextElement();
1232 result.append(key.toString());
1233 result.append('=');
1234 Object value = properties.get(key);
1235 if (value instanceof String[]) {
1236 String[] values = (String[]) value;
1237 result.append('{');
1238 for (int i = 0; i < values.length; i++) {
1239 if (i > 0) {
1240 result.append(',');
1241 }
1242 result.append(values[i].toString());
1243 }
1244 result.append('}');
1245 }
1246 else {
1247 result.append(value.toString());
1248 }
1249 if (enumeration.hasMoreElements()) {
1250 result.append(',');
1251 }
1252 }
1253 result.append(")");
1254 }
1255 }
1256
1257 @Override
1258 public BundleContext getBundleContext() {
1259 return m_context;
1260 }
1261
1262 @Override
1263 public Bundle getBundle() {
1264 return m_bundle;
1265 }
1266
1267 public long getId() {
1268 return m_id;
1269 }
1270
1271 public String getClassName() {
1272 Object serviceInstance = m_componentInstance;
1273 if (serviceInstance != null) {
1274 return serviceInstance.getClass().getName();
1275 }
1276
1277 Object implementation = m_componentDefinition;
1278 if (implementation != null) {
1279 if (implementation instanceof Class) {
1280 return ((Class<?>) implementation).getName();
1281 }
1282 return implementation.getClass().getName();
1283 }
1284
1285 Object instanceFactory = m_instanceFactory;
1286 if (instanceFactory != null) {
1287 return instanceFactory.getClass().getName();
1288 } else {
1289 // unexpected.
1290 return ComponentImpl.class.getName();
1291 }
1292 }
1293
1294 public String[] getServices() {
1295 if (m_serviceName instanceof String[]) {
1296 return (String[]) m_serviceName;
1297 } else if (m_serviceName instanceof String) {
1298 return new String[] { (String) m_serviceName };
1299 } else {
1300 return null;
1301 }
1302 }
1303
1304 public int getState() {
1305 return (isAvailable() ? ComponentDeclaration.STATE_REGISTERED : ComponentDeclaration.STATE_UNREGISTERED);
1306 }
1307
1308 public void ensureNotActive() {
1309 if (m_active.get()) {
1310 throw new IllegalStateException("Can't modify an already started component.");
1311 }
1312 }
1313
1314 public ComponentDeclaration getComponentDeclaration() {
1315 return this;
1316 }
1317
1318 @Override
1319 public String toString() {
1320 if (m_logger.getDebugKey() != null) {
1321 return m_logger.getDebugKey();
1322 }
1323 return getClassName();
1324 }
1325
1326 @Override
1327 public void setThreadPool(Executor threadPool) {
1328 ensureNotActive();
1329 m_executor = new DispatchExecutor(threadPool, m_logger);
1330 }
1331
1332 @Override
1333 public Logger getLogger() {
1334 return m_logger;
1335 }
1336
1337 @Override
1338 public Map<String, Long> getCallbacksTime() {
1339 return m_stopwatch;
1340 }
1341
1342 private Executor getExecutor() {
1343 return m_executor;
1344 }
1345}