atomic-storage: remove type dependency at segment level I/O
[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 static java.util.Objects.requireNonNull;
11
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.concurrent.atomic.AtomicBoolean;
17 import java.util.function.Consumer;
18 import org.apache.aries.blueprint.di.AbstractRecipe;
19 import org.apache.aries.blueprint.di.ExecutionContext;
20 import org.apache.aries.blueprint.di.Recipe;
21 import org.apache.aries.blueprint.ext.DependentComponentFactoryMetadata;
22 import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
23 import org.checkerframework.checker.lock.qual.GuardedBy;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.controller.blueprint.BlueprintContainerRestartService;
26 import org.osgi.framework.ServiceReference;
27 import org.osgi.service.blueprint.container.ComponentDefinitionException;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * Abstract base class for a DependentComponentFactoryMetadata implementation.
33  *
34  * @author Thomas Pantelis
35  */
36 abstract class AbstractDependentComponentFactoryMetadata implements DependentComponentFactoryMetadata {
37     @SuppressFBWarnings("SLF4J_LOGGER_SHOULD_BE_PRIVATE")
38     final Logger log = LoggerFactory.getLogger(getClass());
39     private final String id;
40     private final AtomicBoolean started = new AtomicBoolean();
41     private final AtomicBoolean satisfied = new AtomicBoolean();
42     private final AtomicBoolean restarting = new AtomicBoolean();
43     @GuardedBy("serviceRecipes")
44     private final List<StaticServiceReferenceRecipe> serviceRecipes = new ArrayList<>();
45     private volatile ExtendedBlueprintContainer container;
46     private volatile SatisfactionCallback satisfactionCallback;
47     private volatile String failureMessage;
48     private volatile Throwable failureCause;
49     private volatile String dependencyDesc;
50     @GuardedBy("serviceRecipes")
51     private boolean stoppedServiceRecipes;
52
53     protected AbstractDependentComponentFactoryMetadata(final String id) {
54         this.id = requireNonNull(id);
55     }
56
57     @Override
58     public String getId() {
59         return id;
60     }
61
62     @Override
63     public int getActivation() {
64         return ACTIVATION_EAGER;
65     }
66
67     @Override
68     public List<String> getDependsOn() {
69         return Collections.emptyList();
70     }
71
72     @Override
73     public String getDependencyDescriptor() {
74         return dependencyDesc;
75     }
76
77     @Override
78     public boolean isSatisfied() {
79         return satisfied.get();
80     }
81
82     protected void setFailureMessage(final String failureMessage) {
83         setFailure(failureMessage, null);
84     }
85
86     @SuppressWarnings("checkstyle:hiddenField")
87     protected void setFailure(final String failureMessage, final Throwable failureCause) {
88         this.failureMessage = failureMessage;
89         this.failureCause = failureCause;
90     }
91
92     protected void setDependencyDesc(final String dependencyDesc) {
93         this.dependencyDesc = dependencyDesc;
94     }
95
96     protected final ExtendedBlueprintContainer container() {
97         return container;
98     }
99
100     protected void setSatisfied() {
101         if (satisfied.compareAndSet(false, true)) {
102             satisfactionCallback.notifyChanged();
103         }
104     }
105
106     protected void retrieveService(final String name, final Class<?> interfaceClass,
107             final Consumer<Object> onServiceRetrieved) {
108         retrieveService(name, interfaceClass.getName(), onServiceRetrieved);
109     }
110
111     protected void retrieveService(final String name, final String interfaceName,
112             final Consumer<Object> onServiceRetrieved) {
113         synchronized (serviceRecipes) {
114             if (stoppedServiceRecipes) {
115                 return;
116             }
117
118             StaticServiceReferenceRecipe recipe = new StaticServiceReferenceRecipe(getId() + "-" + name,
119                     container, interfaceName);
120             setDependencyDesc(recipe.getOsgiFilter());
121             serviceRecipes.add(recipe);
122
123             recipe.startTracking(onServiceRetrieved);
124         }
125     }
126
127     protected final String logName() {
128         return (container != null ? container.getBundleContext().getBundle().getSymbolicName() : "") + " (" + id + ")";
129     }
130
131     @Override
132     public void init(final ExtendedBlueprintContainer newContainer) {
133         this.container = newContainer;
134
135         log.debug("{}: In init", logName());
136     }
137
138     protected void onCreate() throws ComponentDefinitionException {
139         if (failureMessage != null) {
140             throw new ComponentDefinitionException(failureMessage, failureCause);
141         }
142
143         // The following code is a bit odd so requires some explanation. A little background... If a bean
144         // is a prototype then the corresponding Recipe create method does not register the bean as created
145         // with the BlueprintRepository and thus the destroy method isn't called on container destroy. We
146         // rely on destroy being called to close our DTCL registration. Unfortunately the default setting
147         // for the prototype flag in AbstractRecipe is true and the DependentComponentFactoryRecipe, which
148         // is created for DependentComponentFactoryMetadata types of which we are one, doesn't have a way for
149         // us to indicate the prototype state via our metadata.
150         //
151         // The ExecutionContext is actually backed by the BlueprintRepository so we access it here to call
152         // the removePartialObject method which removes any partially created instance, which does not apply
153         // in our case, and also has the side effect of registering our bean as created as if it wasn't a
154         // prototype. We also obtain our corresponding Recipe instance and clear the prototype flag. This
155         // doesn't look to be necessary but is done so for completeness. Better late than never. Note we have
156         // to do this here rather than in startTracking b/c the ExecutionContext is not available yet at that
157         // point.
158         //
159         // Now the stopTracking method is called on container destroy but startTracking/stopTracking can also
160         // be called multiple times during the container creation process for Satisfiable recipes as bean
161         // processors may modify the metadata which could affect how dependencies are satisfied. An example of
162         // this is with service references where the OSGi filter metadata can be modified by bean processors
163         // after the initial service dependency is satisfied. However we don't have any metadata that could
164         // be modified by a bean processor and we don't want to register/unregister our DTCL multiple times
165         // so we only process startTracking once and close the DTCL registration once on container destroy.
166         ExecutionContext executionContext = ExecutionContext.Holder.getContext();
167         executionContext.removePartialObject(id);
168
169         Recipe myRecipe = executionContext.getRecipe(id);
170         if (myRecipe instanceof AbstractRecipe) {
171             log.debug("{}: setPrototype to false", logName());
172             ((AbstractRecipe)myRecipe).setPrototype(false);
173         } else {
174             log.warn("{}: Recipe is null or not an AbstractRecipe", logName());
175         }
176     }
177
178     protected abstract void startTracking();
179
180     @Override
181     public final void startTracking(final SatisfactionCallback newSatisfactionCallback) {
182         if (!started.compareAndSet(false, true)) {
183             return;
184         }
185
186         log.debug("{}: In startTracking", logName());
187
188         this.satisfactionCallback = newSatisfactionCallback;
189
190         startTracking();
191     }
192
193     @Override
194     public void stopTracking() {
195         log.debug("{}: In stopTracking", logName());
196
197         stopServiceRecipes();
198     }
199
200     @Override
201     public void destroy(final Object instance) {
202         log.debug("{}: In destroy", logName());
203
204         stopServiceRecipes();
205     }
206
207     private void stopServiceRecipes() {
208         synchronized (serviceRecipes) {
209             stoppedServiceRecipes = true;
210             for (StaticServiceReferenceRecipe recipe: serviceRecipes) {
211                 recipe.stop();
212             }
213
214             serviceRecipes.clear();
215         }
216     }
217
218     protected void restartContainer() {
219         if (restarting.compareAndSet(false, true)) {
220             BlueprintContainerRestartService restartService = getOSGiService(BlueprintContainerRestartService.class);
221             if (restartService != null) {
222                 log.debug("{}: Restarting container", logName());
223                 restartService.restartContainerAndDependents(container().getBundleContext().getBundle());
224             }
225         }
226     }
227
228     @SuppressWarnings("unchecked")
229     protected <T> @Nullable T getOSGiService(final Class<T> serviceInterface) {
230         try {
231             ServiceReference<T> serviceReference =
232                     container().getBundleContext().getServiceReference(serviceInterface);
233             if (serviceReference == null) {
234                 log.warn("{}: {} reference not found", logName(), serviceInterface.getSimpleName());
235                 return null;
236             }
237
238             T service = (T)container().getService(serviceReference);
239             if (service == null) {
240                 // This could happen on shutdown if the service was already unregistered so we log as debug.
241                 log.debug("{}: {} was not found", logName(), serviceInterface.getSimpleName());
242             }
243
244             return service;
245         } catch (final IllegalStateException e) {
246             // This is thrown if the BundleContext is no longer valid which is possible on shutdown so we
247             // log as debug.
248             log.debug("{}: Error obtaining {}", logName(), serviceInterface.getSimpleName(), e);
249         }
250
251         return null;
252     }
253 }