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 javax.annotation.Nullable;
17 import org.apache.aries.blueprint.di.AbstractRecipe;
18 import org.apache.aries.blueprint.di.ExecutionContext;
19 import org.apache.aries.blueprint.di.Recipe;
20 import org.apache.aries.blueprint.ext.DependentComponentFactoryMetadata;
21 import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
22 import org.opendaylight.controller.blueprint.BlueprintContainerRestartService;
23 import org.osgi.framework.ServiceReference;
24 import org.osgi.service.blueprint.container.ComponentDefinitionException;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * @author Thomas Pantelis
31 abstract class AbstractDependentComponentFactoryMetadata implements DependentComponentFactoryMetadata {
32 private final Logger log = LoggerFactory.getLogger(getClass());
33 private final String id;
34 private final AtomicBoolean started = new AtomicBoolean();
35 private final AtomicBoolean satisfied = new AtomicBoolean();
36 private final AtomicBoolean restarting = new AtomicBoolean();
37 private final List<StaticServiceReferenceRecipe> serviceRecipes = new ArrayList<>();
38 private volatile ExtendedBlueprintContainer container;
39 private volatile SatisfactionCallback satisfactionCallback;
40 private volatile String failureMessage;
41 private volatile Throwable failureCause;
42 private volatile String dependendencyDesc;
44 protected AbstractDependentComponentFactoryMetadata(String id) {
45 this.id = Preconditions.checkNotNull(id);
49 public String getId() {
54 public int getActivation() {
55 return ACTIVATION_EAGER;
59 public List<String> getDependsOn() {
60 return Collections.emptyList();
64 public String getDependencyDescriptor() {
65 return dependendencyDesc;
69 public boolean isSatisfied() {
70 return satisfied.get();
73 protected abstract void startTracking();
75 protected void setFailureMessage(String failureMessage) {
76 setFailure(failureMessage, null);
79 protected void setFailure(String failureMessage, Throwable failureCause) {
80 this.failureMessage = failureMessage;
81 this.failureCause = failureCause;
84 protected void setDependendencyDesc(String dependendencyDesc) {
85 this.dependendencyDesc = dependendencyDesc;
88 protected final ExtendedBlueprintContainer container() {
92 protected void setSatisfied() {
93 if(satisfied.compareAndSet(false, true)) {
94 satisfactionCallback.notifyChanged();
98 protected void retrieveService(String name, Class<?> interfaceClass, Consumer<Object> onServiceRetrieved) {
99 retrieveService(name, interfaceClass.getName(), onServiceRetrieved);
102 protected void retrieveService(String name, String interfaceName, Consumer<Object> onServiceRetrieved) {
103 StaticServiceReferenceRecipe recipe = new StaticServiceReferenceRecipe(getId() + "-" + name,
104 container, interfaceName);
105 setDependendencyDesc(recipe.getOsgiFilter());
106 serviceRecipes.add(recipe);
108 recipe.startTracking(onServiceRetrieved);
111 protected final String logName() {
112 return (container != null ? container.getBundleContext().getBundle().getSymbolicName() : "") +
117 public void init(ExtendedBlueprintContainer container) {
118 this.container = container;
120 log.debug("{}: In init", logName());
123 protected void onCreate() throws ComponentDefinitionException {
124 if(failureMessage != null) {
125 throw new ComponentDefinitionException(failureMessage, failureCause);
128 // The following code is a bit odd so requires some explanation. A little background... If a bean
129 // is a prototype then the corresponding Recipe create method does not register the bean as created
130 // with the BlueprintRepository and thus the destroy method isn't called on container destroy. We
131 // rely on destroy being called to close our DTCL registration. Unfortunately the default setting
132 // for the prototype flag in AbstractRecipe is true and the DependentComponentFactoryRecipe, which
133 // is created for DependentComponentFactoryMetadata types of which we are one, doesn't have a way for
134 // us to indicate the prototype state via our metadata.
136 // The ExecutionContext is actually backed by the BlueprintRepository so we access it here to call
137 // the removePartialObject method which removes any partially created instance, which does not apply
138 // in our case, and also has the side effect of registering our bean as created as if it wasn't a
139 // prototype. We also obtain our corresponding Recipe instance and clear the prototype flag. This
140 // doesn't look to be necessary but is done so for completeness. Better late than never. Note we have
141 // to do this here rather than in startTracking b/c the ExecutionContext is not available yet at that
144 // Now the stopTracking method is called on container destroy but startTracking/stopTracking can also
145 // be called multiple times during the container creation process for Satisfiable recipes as bean
146 // processors may modify the metadata which could affect how dependencies are satisfied. An example of
147 // this is with service references where the OSGi filter metadata can be modified by bean processors
148 // after the initial service dependency is satisfied. However we don't have any metadata that could
149 // be modified by a bean processor and we don't want to register/unregister our DTCL multiple times
150 // so we only process startTracking once and close the DTCL registration once on container destroy.
151 ExecutionContext executionContext = ExecutionContext.Holder.getContext();
152 executionContext.removePartialObject(id);
154 Recipe myRecipe = executionContext.getRecipe(id);
155 if(myRecipe instanceof AbstractRecipe) {
156 log.debug("{}: setPrototype to false", logName());
157 ((AbstractRecipe)myRecipe).setPrototype(false);
159 log.warn("{}: Recipe is null or not an AbstractRecipe", logName());
164 public final void startTracking(final SatisfactionCallback satisfactionCallback) {
165 if(!started.compareAndSet(false, true)) {
169 log.debug("{}: In startTracking", logName());
171 this.satisfactionCallback = satisfactionCallback;
177 public void stopTracking() {
178 log.debug("{}: In stopTracking", logName());
180 stopServiceRecipes();
184 public void destroy(Object instance) {
185 log.debug("{}: In destroy", logName());
187 stopServiceRecipes();
190 private void stopServiceRecipes() {
191 for(StaticServiceReferenceRecipe recipe: serviceRecipes) {
195 serviceRecipes.clear();
198 protected void restartContainer() {
199 if(restarting.compareAndSet(false, true)) {
200 BlueprintContainerRestartService restartService = getOSGiService(BlueprintContainerRestartService.class);
201 if(restartService != null) {
202 log.debug("{}: Restarting container", logName());
203 restartService.restartContainerAndDependents(container().getBundleContext().getBundle());
208 @SuppressWarnings("unchecked")
210 protected <T> T getOSGiService(Class<T> serviceInterface) {
212 ServiceReference<T> serviceReference =
213 container().getBundleContext().getServiceReference(serviceInterface);
214 if(serviceReference == null) {
215 log.warn("{}: {} reference not found", logName(), serviceInterface.getSimpleName());
219 T service = (T)container().getService(serviceReference);
220 if(service == null) {
221 // This could happen on shutdown if the service was already unregistered so we log as debug.
222 log.debug("{}: {} was not found", logName(), serviceInterface.getSimpleName());
226 } catch(IllegalStateException e) {
227 // This is thrown if the BundleContext is no longer valid which is possible on shutdown so we
229 log.debug("{}: Error obtaining {}", logName(), serviceInterface.getSimpleName(), e);