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