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