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