ecb02a1c4f1e1acc17076b3e17ee2791211dedc1
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / dependencyresolver / DependencyResolverImpl.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 java.lang.String.format;
11
12 import java.util.HashSet;
13 import java.util.LinkedHashSet;
14 import java.util.Set;
15
16 import javax.annotation.concurrent.GuardedBy;
17 import javax.management.ObjectName;
18
19 import org.opendaylight.controller.config.api.DependencyResolver;
20 import org.opendaylight.controller.config.api.JmxAttribute;
21 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
22 import org.opendaylight.controller.config.api.ModuleIdentifier;
23 import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
24 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
25 import org.opendaylight.controller.config.manager.impl.TransactionStatus;
26 import org.opendaylight.controller.config.spi.Module;
27 import org.opendaylight.controller.config.spi.ModuleFactory;
28
29 /**
30  * Protect {@link org.opendaylight.controller.config.spi.Module#getInstance()}
31  * by creating proxy that would throw exception if those methods are called
32  * during validation. Tracks dependencies for ordering purposes.
33  */
34 final class DependencyResolverImpl implements DependencyResolver,
35        Comparable<DependencyResolverImpl> {
36     private final ModulesHolder modulesHolder;
37     private final ModuleIdentifier name;
38     private final TransactionStatus transactionStatus;
39     @GuardedBy("this")
40     private final Set<ModuleIdentifier> dependencies = new HashSet<>();
41
42     DependencyResolverImpl(ModuleIdentifier currentModule,
43             TransactionStatus transactionStatus, ModulesHolder modulesHolder) {
44         this.name = currentModule;
45         this.transactionStatus = transactionStatus;
46         this.modulesHolder = modulesHolder;
47     }
48
49     public ModuleIdentifier getName() {
50         return name;
51     }
52
53     /**
54      * {@inheritDoc}
55      */
56     @Override
57     public void validateDependency(
58             Class<? extends AbstractServiceInterface> expectedServiceInterface,
59             ObjectName dependentModuleReadOnlyON, JmxAttribute jmxAttribute) {
60
61         transactionStatus.checkNotCommitted();
62         if (expectedServiceInterface == null) {
63             throw new NullPointerException(
64                     "Parameter 'expectedServiceInterface' is null");
65         }
66         if (jmxAttribute == null)
67             throw new NullPointerException("Parameter 'jmxAttribute' is null");
68
69         JmxAttributeValidationException.checkNotNull(dependentModuleReadOnlyON,
70                 "is null, " + "expected dependency implementing "
71                         + expectedServiceInterface, jmxAttribute);
72
73         // check that objectName belongs to this transaction - this should be
74         // stripped
75         // in DynamicWritableWrapper
76         boolean hasTransaction = ObjectNameUtil
77                 .getTransactionName(dependentModuleReadOnlyON) != null;
78         JmxAttributeValidationException.checkCondition(
79                 hasTransaction == false,
80                 format("ObjectName should not contain "
81                         + "transaction name. %s set to %s. ", jmxAttribute,
82                         dependentModuleReadOnlyON), jmxAttribute);
83
84         ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentModuleReadOnlyON, ObjectNameUtil
85                 .TYPE_MODULE);
86
87         ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
88
89         boolean implementsSI = foundFactory
90                 .isModuleImplementingServiceInterface(expectedServiceInterface);
91         if (implementsSI == false) {
92             String message = format(
93                     "Found module factory does not expose expected service interface. "
94                             + "Module name is %s : %s, expected service interface %s, dependent module ON %s , "
95                             + "attribute %s",
96                     foundFactory.getImplementationName(), foundFactory,
97                     expectedServiceInterface, dependentModuleReadOnlyON,
98                     jmxAttribute);
99             throw new JmxAttributeValidationException(message, jmxAttribute);
100         }
101         synchronized (this) {
102             dependencies.add(moduleIdentifier);
103         }
104     }
105
106     /**
107      * {@inheritDoc}
108      */
109     @Override
110     public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentON,
111             JmxAttribute jmxAttribute) {
112         if (expectedType == null || dependentON == null || jmxAttribute == null) {
113             throw new IllegalArgumentException(format(
114                     "Null parameters not allowed, got {} {} {}", expectedType,
115                     dependentON, jmxAttribute));
116         }
117
118         transactionStatus.checkCommitStarted();
119         transactionStatus.checkNotCommitted();
120
121         ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
122                 dependentON, ObjectNameUtil.TYPE_MODULE);
123         Module module = modulesHolder.findModule(dependentModuleIdentifier,
124                 jmxAttribute);
125         synchronized (this) {
126             dependencies.add(dependentModuleIdentifier);
127         }
128         AutoCloseable instance = module.getInstance();
129         if (instance == null) {
130             String message = format(
131                     "Error while %s resolving instance %s. getInstance() returned null. "
132                             + "Expected type %s , attribute %s", name,
133                     dependentModuleIdentifier, expectedType, jmxAttribute);
134             throw new JmxAttributeValidationException(message, jmxAttribute);
135         }
136         try {
137             T result = expectedType.cast(instance);
138             return result;
139         } catch (ClassCastException e) {
140             String message = format(
141                     "Instance cannot be cast to expected type. Instance class is %s , "
142                             + "expected type %s , attribute %s",
143                     instance.getClass(), expectedType, jmxAttribute);
144             throw new JmxAttributeValidationException(message, e, jmxAttribute);
145         }
146     }
147
148     @Override
149     public int compareTo(DependencyResolverImpl o) {
150         transactionStatus.checkCommitted();
151         return Integer.compare(getMaxDependencyDepth(),
152                 o.getMaxDependencyDepth());
153     }
154
155     private Integer maxDependencyDepth;
156
157     int getMaxDependencyDepth() {
158         if (maxDependencyDepth == null) {
159             throw new IllegalStateException("Dependency depth was not computed");
160         }
161         return maxDependencyDepth;
162     }
163
164     public void countMaxDependencyDepth(DependencyResolverManager manager) {
165         transactionStatus.checkCommitted();
166         if (maxDependencyDepth == null) {
167             maxDependencyDepth = getMaxDepth(this, manager,
168                     new LinkedHashSet<ModuleIdentifier>());
169         }
170     }
171
172     private static int getMaxDepth(DependencyResolverImpl impl,
173             DependencyResolverManager manager,
174             LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
175         int maxDepth = 0;
176         LinkedHashSet<ModuleIdentifier> chainForDetectingCycles2 = new LinkedHashSet<>(
177                 chainForDetectingCycles);
178         chainForDetectingCycles2.add(impl.getName());
179         for (ModuleIdentifier dependencyName : impl.dependencies) {
180             DependencyResolverImpl dependentDRI = manager
181                     .getOrCreate(dependencyName);
182             if (chainForDetectingCycles2.contains(dependencyName)) {
183                 throw new IllegalStateException(format(
184                         "Cycle detected, {} contains {}",
185                         chainForDetectingCycles2, dependencyName));
186             }
187             int subDepth;
188             if (dependentDRI.maxDependencyDepth != null) {
189                 subDepth = dependentDRI.maxDependencyDepth;
190             } else {
191                 subDepth = getMaxDepth(dependentDRI, manager,
192                         chainForDetectingCycles2);
193                 dependentDRI.maxDependencyDepth = subDepth;
194             }
195             if (subDepth + 1 > maxDepth) {
196                 maxDepth = subDepth + 1;
197             }
198         }
199         impl.maxDependencyDepth = maxDepth;
200         return maxDepth;
201     }
202 }