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