2 * Copyright (c) 2016 Brocade Communications Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.blueprint.ext;
10 import com.google.common.base.Preconditions;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.concurrent.atomic.AtomicBoolean;
15 import java.util.function.Consumer;
16 import org.apache.aries.blueprint.di.AbstractRecipe;
17 import org.apache.aries.blueprint.di.ExecutionContext;
18 import org.apache.aries.blueprint.di.Recipe;
19 import org.apache.aries.blueprint.ext.DependentComponentFactoryMetadata;
20 import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
21 import org.osgi.service.blueprint.container.ComponentDefinitionException;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
26 * @author Thomas Pantelis
28 abstract class AbstractDependentComponentFactoryMetadata implements DependentComponentFactoryMetadata {
29 private final Logger log = LoggerFactory.getLogger(getClass());
30 private final String id;
31 private final AtomicBoolean started = new AtomicBoolean();
32 private final AtomicBoolean satisfied = new AtomicBoolean();
33 private final List<StaticServiceReferenceRecipe> serviceRecipes = new ArrayList<>();
34 private volatile ExtendedBlueprintContainer container;
35 private volatile SatisfactionCallback satisfactionCallback;
36 private volatile String failureMessage;
37 private volatile String dependendencyDesc;
39 protected AbstractDependentComponentFactoryMetadata(String id) {
40 this.id = Preconditions.checkNotNull(id);
44 public String getId() {
49 public int getActivation() {
50 return ACTIVATION_EAGER;
54 public List<String> getDependsOn() {
55 return Collections.emptyList();
59 public String getDependencyDescriptor() {
60 return dependendencyDesc;
64 public boolean isSatisfied() {
65 return satisfied.get();
68 protected abstract void startTracking();
70 protected void setFailureMessage(String failureMessage) {
71 this.failureMessage = failureMessage;
74 protected void setDependendencyDesc(String dependendencyDesc) {
75 this.dependendencyDesc = dependendencyDesc;
78 protected final ExtendedBlueprintContainer container() {
82 protected void setSatisfied() {
84 satisfactionCallback.notifyChanged();
87 protected void retrieveService(String name, Class<?> interfaceClass, Consumer<Object> onServiceRetrieved) {
88 StaticServiceReferenceRecipe recipe = new StaticServiceReferenceRecipe(getId() + "-" + name,
89 container, interfaceClass.getName());
90 setDependendencyDesc(recipe.getOsgiFilter());
91 serviceRecipes.add(recipe);
93 recipe.startTracking(onServiceRetrieved);
96 protected final String logName() {
97 return (container != null ? container.getBundleContext().getBundle().getSymbolicName() : "") +
102 public void init(ExtendedBlueprintContainer container) {
103 this.container = container;
105 log.debug("{}: In init", logName());
108 protected void onCreate() throws ComponentDefinitionException {
109 if(failureMessage != null) {
110 throw new ComponentDefinitionException(failureMessage);
113 // The following code is a bit odd so requires some explanation. A little background... If a bean
114 // is a prototype then the corresponding Recipe create method does not register the bean as created
115 // with the BlueprintRepository and thus the destroy method isn't called on container destroy. We
116 // rely on destroy being called to close our DTCL registration. Unfortunately the default setting
117 // for the prototype flag in AbstractRecipe is true and the DependentComponentFactoryRecipe, which
118 // is created for DependentComponentFactoryMetadata types of which we are one, doesn't have a way for
119 // us to indicate the prototype state via our metadata.
121 // The ExecutionContext is actually backed by the BlueprintRepository so we access it here to call
122 // the removePartialObject method which removes any partially created instance, which does not apply
123 // in our case, and also has the side effect of registering our bean as created as if it wasn't a
124 // prototype. We also obtain our corresponding Recipe instance and clear the prototype flag. This
125 // doesn't look to be necessary but is done so for completeness. Better late than never. Note we have
126 // to do this here rather than in startTracking b/c the ExecutionContext is not available yet at that
129 // Now the stopTracking method is called on container destroy but startTracking/stopTracking can also
130 // be called multiple times during the container creation process for Satisfiable recipes as bean
131 // processors may modify the metadata which could affect how dependencies are satisfied. An example of
132 // this is with service references where the OSGi filter metadata can be modified by bean processors
133 // after the initial service dependency is satisfied. However we don't have any metadata that could
134 // be modified by a bean processor and we don't want to register/unregister our DTCL multiple times
135 // so we only process startTracking once and close the DTCL registration once on container destroy.
136 ExecutionContext executionContext = ExecutionContext.Holder.getContext();
137 executionContext.removePartialObject(id);
139 Recipe myRecipe = executionContext.getRecipe(id);
140 if(myRecipe instanceof AbstractRecipe) {
141 log.debug("{}: setPrototype to false", logName());
142 ((AbstractRecipe)myRecipe).setPrototype(false);
144 log.warn("{}: Recipe is null or not an AbstractRecipe", logName());
149 public final void startTracking(final SatisfactionCallback satisfactionCallback) {
150 if(!started.compareAndSet(false, true)) {
154 log.debug("{}: In startTracking", logName());
156 this.satisfactionCallback = satisfactionCallback;
162 public void stopTracking() {
163 log.debug("{}: In stopTracking", logName());
165 stopServiceRecipes();
169 public void destroy(Object instance) {
170 log.debug("{}: In destroy", logName());
172 stopServiceRecipes();
175 private void stopServiceRecipes() {
176 for(StaticServiceReferenceRecipe recipe: serviceRecipes) {
180 serviceRecipes.clear();