3ef6e5d8421ac39834394b34afbf56d58b7eeca8
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / dependencyresolver / DependencyResolverManager.java
1 /*
2  * Copyright (c) 2013 Cisco 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.config.manager.impl.dependencyresolver;
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import com.google.common.base.Preconditions;
13 import com.google.common.reflect.AbstractInvocationHandler;
14 import com.google.common.reflect.Reflection;
15 import java.lang.reflect.InvocationTargetException;
16 import java.lang.reflect.Method;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import javax.annotation.concurrent.GuardedBy;
23 import javax.management.InstanceAlreadyExistsException;
24 import javax.management.MBeanServer;
25 import org.opendaylight.controller.config.api.DependencyResolver;
26 import org.opendaylight.controller.config.api.DependencyResolverFactory;
27 import org.opendaylight.controller.config.api.JmxAttribute;
28 import org.opendaylight.controller.config.api.ModuleIdentifier;
29 import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
30 import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
31 import org.opendaylight.controller.config.manager.impl.CommitInfo;
32 import org.opendaylight.controller.config.manager.impl.DeadlockMonitor;
33 import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo;
34 import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
35 import org.opendaylight.controller.config.manager.impl.TransactionStatus;
36 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
37 import org.opendaylight.controller.config.manager.impl.osgi.mapping.BindingContextProvider;
38 import org.opendaylight.controller.config.spi.Module;
39 import org.opendaylight.controller.config.spi.ModuleFactory;
40 import org.osgi.framework.BundleContext;
41
42 /**
43  * Holds information about modules being created and destroyed within this
44  * transaction. Observes usage of DependencyResolver within modules to figure
45  * out dependency tree.
46  */
47 public class DependencyResolverManager implements DependencyResolverFactory, AutoCloseable {
48     @GuardedBy("this")
49     private final Map<ModuleIdentifier, DependencyResolverImpl> moduleIdentifiersToDependencyResolverMap = new HashMap<>();
50     private final TransactionIdentifier transactionIdentifier;
51     private final ModulesHolder modulesHolder;
52     private final TransactionStatus transactionStatus;
53     private final ServiceReferenceReadableRegistry readableRegistry;
54     private final BindingContextProvider bindingContextProvider;
55     private final DeadlockMonitor deadlockMonitor;
56     private final MBeanServer mBeanServer;
57
58     public DependencyResolverManager(final TransactionIdentifier transactionIdentifier,
59                                      final TransactionStatus transactionStatus,
60                                      final ServiceReferenceReadableRegistry readableRegistry, final BindingContextProvider bindingContextProvider,
61                                      final MBeanServer mBeanServer) {
62         this.transactionIdentifier = transactionIdentifier;
63         this.modulesHolder = new ModulesHolder(transactionIdentifier);
64         this.transactionStatus = transactionStatus;
65         this.readableRegistry = readableRegistry;
66         this.bindingContextProvider = bindingContextProvider;
67         this.deadlockMonitor = new DeadlockMonitor(transactionIdentifier);
68         this.mBeanServer = mBeanServer;
69     }
70
71     @Override
72     public DependencyResolver createDependencyResolver(final ModuleIdentifier moduleIdentifier) {
73         return getOrCreate(moduleIdentifier);
74     }
75
76     public synchronized DependencyResolverImpl getOrCreate(final ModuleIdentifier name) {
77         DependencyResolverImpl dependencyResolver = moduleIdentifiersToDependencyResolverMap.get(name);
78         if (dependencyResolver == null) {
79             transactionStatus.checkNotCommitted();
80             dependencyResolver = new DependencyResolverImpl(name, transactionStatus, modulesHolder, readableRegistry,
81                     bindingContextProvider, transactionIdentifier.getName(), mBeanServer);
82             moduleIdentifiersToDependencyResolverMap.put(name, dependencyResolver);
83         }
84         return dependencyResolver;
85     }
86
87     /**
88      * Get all dependency resolvers, including those that belong to destroyed
89      * things?
90      */
91     private List<DependencyResolverImpl> getAllSorted() {
92         transactionStatus.checkCommitStarted();
93         List<DependencyResolverImpl> sorted = new ArrayList<>(
94                 moduleIdentifiersToDependencyResolverMap.values());
95         for (DependencyResolverImpl dri : sorted) {
96             dri.countMaxDependencyDepth(this);
97         }
98         Collections.sort(sorted);
99         return sorted;
100     }
101
102     public List<ModuleIdentifier> getSortedModuleIdentifiers() {
103         List<ModuleIdentifier> result = new ArrayList<>(
104                 moduleIdentifiersToDependencyResolverMap.size());
105         for (DependencyResolverImpl dri : getAllSorted()) {
106             ModuleIdentifier driName = dri.getIdentifier();
107             result.add(driName);
108         }
109         return result;
110     }
111
112     public ModuleInternalTransactionalInfo destroyModule(
113             final ModuleIdentifier moduleIdentifier) {
114         transactionStatus.checkNotCommitted();
115         ModuleInternalTransactionalInfo found = modulesHolder
116                 .destroyModule(moduleIdentifier);
117         moduleIdentifiersToDependencyResolverMap.remove(moduleIdentifier);
118         return found;
119     }
120
121     // protect write access
122
123     private static final class ModuleInvocationHandler extends AbstractInvocationHandler {
124         private final DeadlockMonitor deadlockMonitor;
125         private final ModuleIdentifier moduleIdentifier;
126         private final Module module;
127
128         // optimization: subsequent calls to getInstance MUST return the same value during transaction,
129         // so it is safe to cache the response
130         private Object cachedInstance;
131
132         ModuleInvocationHandler(final DeadlockMonitor deadlockMonitor, final ModuleIdentifier moduleIdentifier, final Module module) {
133             this.deadlockMonitor = Preconditions.checkNotNull(deadlockMonitor);
134             this.moduleIdentifier = Preconditions.checkNotNull(moduleIdentifier);
135             this.module = Preconditions.checkNotNull(module);
136         }
137
138         @Override
139         protected Object handleInvocation(final Object proxy, final Method method, final Object[] args) throws Throwable {
140             boolean isGetInstance = "getInstance".equals(method.getName());
141             if (isGetInstance) {
142                 if (cachedInstance != null) {
143                     return cachedInstance;
144                 }
145
146                 checkState(deadlockMonitor.isAlive(), "Deadlock monitor is not alive");
147                 deadlockMonitor.setCurrentlyInstantiatedModule(moduleIdentifier);
148             }
149             try {
150                 Object response = method.invoke(module, args);
151                 if (isGetInstance) {
152                     cachedInstance = response;
153                 }
154                 return response;
155             } catch(InvocationTargetException e) {
156                 throw e.getCause();
157             } finally {
158                 if (isGetInstance) {
159                     deadlockMonitor.setCurrentlyInstantiatedModule(null);
160                 }
161             }
162         }
163     }
164
165     public void put(
166             final ModuleIdentifier moduleIdentifier,
167             final Module module,
168             final ModuleFactory moduleFactory,
169             final ModuleInternalInfo maybeOldInternalInfo,
170             final TransactionModuleJMXRegistration transactionModuleJMXRegistration,
171             final boolean isDefaultBean, final BundleContext bundleContext) {
172         transactionStatus.checkNotCommitted();
173
174         Class<? extends Module> moduleClass = Module.class;
175         if (module instanceof RuntimeBeanRegistratorAwareModule) {
176             moduleClass = RuntimeBeanRegistratorAwareModule.class;
177         }
178         Module proxiedModule = Reflection.newProxy(moduleClass, new ModuleInvocationHandler(deadlockMonitor, moduleIdentifier, module));
179         ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
180                 moduleIdentifier, proxiedModule, moduleFactory,
181                 maybeOldInternalInfo, transactionModuleJMXRegistration, isDefaultBean, module, bundleContext);
182         modulesHolder.put(moduleInternalTransactionalInfo);
183     }
184
185     // wrapped methods:
186
187     public CommitInfo toCommitInfo() {
188         return modulesHolder.toCommitInfo();
189     }
190
191     public Module findModule(final ModuleIdentifier moduleIdentifier,
192                              final JmxAttribute jmxAttributeForReporting) {
193         return modulesHolder.findModule(moduleIdentifier,
194                 jmxAttributeForReporting);
195     }
196
197     public ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(final ModuleIdentifier moduleIdentifier) {
198         return modulesHolder.findModuleInternalTransactionalInfo(moduleIdentifier);
199     }
200
201     public ModuleFactory findModuleFactory(final ModuleIdentifier moduleIdentifier,
202                                            final JmxAttribute jmxAttributeForReporting) {
203         return modulesHolder.findModuleFactory(moduleIdentifier,
204                 jmxAttributeForReporting);
205     }
206
207     public Map<ModuleIdentifier, Module> getAllModules() {
208         return modulesHolder.getAllModules();
209     }
210
211     public void assertNotExists(final ModuleIdentifier moduleIdentifier)
212             throws InstanceAlreadyExistsException {
213         modulesHolder.assertNotExists(moduleIdentifier);
214     }
215
216     public List<ModuleIdentifier> findAllByFactory(final ModuleFactory factory) {
217         List<ModuleIdentifier> result = new ArrayList<>();
218         for (ModuleInternalTransactionalInfo info : modulesHolder.getAllInfos()) {
219             if (factory.equals(info.getModuleFactory())) {
220                 result.add(info.getIdentifier());
221             }
222         }
223         return result;
224     }
225
226     @Override
227     public void close() {
228         deadlockMonitor.close();
229     }
230
231 }