Wait for RPCService registered in RpcServiceMetadata
[controller.git] / opendaylight / blueprint / src / main / java / org / opendaylight / controller / blueprint / ext / AbstractDependentComponentFactoryMetadata.java
1 /*
2  * Copyright (c) 2016 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.blueprint.ext;
9
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;
24
25 /**
26  * @author Thomas Pantelis
27  */
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;
38
39     protected AbstractDependentComponentFactoryMetadata(String id) {
40         this.id = Preconditions.checkNotNull(id);
41     }
42
43     @Override
44     public String getId() {
45         return id;
46     }
47
48     @Override
49     public int getActivation() {
50         return ACTIVATION_EAGER;
51     }
52
53     @Override
54     public List<String> getDependsOn() {
55         return Collections.emptyList();
56     }
57
58     @Override
59     public String getDependencyDescriptor() {
60         return dependendencyDesc;
61     }
62
63     @Override
64     public boolean isSatisfied() {
65         return satisfied.get();
66     }
67
68     protected abstract void startTracking();
69
70     protected void setFailureMessage(String failureMessage) {
71         this.failureMessage = failureMessage;
72     }
73
74     protected void setDependendencyDesc(String dependendencyDesc) {
75         this.dependendencyDesc = dependendencyDesc;
76     }
77
78     protected final ExtendedBlueprintContainer container() {
79         return container;
80     }
81
82     protected void setSatisfied() {
83         satisfied.set(true);
84         satisfactionCallback.notifyChanged();
85     }
86
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);
92
93         recipe.startTracking(onServiceRetrieved);
94     }
95
96     protected final String logName() {
97         return (container != null ? container.getBundleContext().getBundle().getSymbolicName() : "") +
98                 " (" + id + ")";
99     }
100
101     @Override
102     public void init(ExtendedBlueprintContainer container) {
103         this.container = container;
104
105         log.debug("{}: In init", logName());
106     }
107
108     protected void onCreate() throws ComponentDefinitionException {
109         if(failureMessage != null) {
110             throw new ComponentDefinitionException(failureMessage);
111         }
112
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.
120         //
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
127         // point.
128         //
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);
138
139         Recipe myRecipe = executionContext.getRecipe(id);
140         if(myRecipe instanceof AbstractRecipe) {
141             log.debug("{}: setPrototype to false", logName());
142             ((AbstractRecipe)myRecipe).setPrototype(false);
143         } else {
144             log.warn("{}: Recipe is null or not an AbstractRecipe", logName());
145         }
146     }
147
148     @Override
149     public final void startTracking(final SatisfactionCallback satisfactionCallback) {
150         if(!started.compareAndSet(false, true)) {
151             return;
152         }
153
154         log.debug("{}: In startTracking", logName());
155
156         this.satisfactionCallback = satisfactionCallback;
157
158         startTracking();
159     }
160
161     @Override
162     public void stopTracking() {
163         log.debug("{}: In stopTracking", logName());
164
165         stopServiceRecipes();
166     }
167
168     @Override
169     public void destroy(Object instance) {
170         log.debug("{}: In destroy", logName());
171
172         stopServiceRecipes();
173     }
174
175     private void stopServiceRecipes() {
176         for(StaticServiceReferenceRecipe recipe: serviceRecipes) {
177             recipe.stop();
178         }
179
180         serviceRecipes.clear();
181     }
182 }