Initial code drop of yang model driven configuration system
[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 import org.opendaylight.protocol.concepts.NamedObject;
29
30 /**
31  * Protect {@link org.opendaylight.controller.config.spi.Module#getInstance()}
32  * by creating proxy that would throw exception if those methods are called
33  * during validation. Tracks dependencies for ordering purposes.
34  */
35 final class DependencyResolverImpl implements DependencyResolver,
36         NamedObject<ModuleIdentifier>, Comparable<DependencyResolverImpl> {
37     private final ModulesHolder modulesHolder;
38     private final ModuleIdentifier name;
39     private final TransactionStatus transactionStatus;
40     @GuardedBy("this")
41     private final Set<ModuleIdentifier> dependencies = new HashSet<>();
42
43     DependencyResolverImpl(ModuleIdentifier currentModule,
44             TransactionStatus transactionStatus, ModulesHolder modulesHolder) {
45         this.name = currentModule;
46         this.transactionStatus = transactionStatus;
47         this.modulesHolder = modulesHolder;
48     }
49
50     @Override
51     public ModuleIdentifier getName() {
52         return name;
53     }
54
55     /**
56      * {@inheritDoc}
57      */
58     @Override
59     public void validateDependency(
60             Class<? extends AbstractServiceInterface> expectedServiceInterface,
61             ObjectName dependentModuleReadOnlyON, JmxAttribute jmxAttribute) {
62
63         transactionStatus.checkNotCommitted();
64         if (expectedServiceInterface == null) {
65             throw new NullPointerException(
66                     "Parameter 'expectedServiceInterface' is null");
67         }
68         if (jmxAttribute == null)
69             throw new NullPointerException("Parameter 'jmxAttribute' is null");
70
71         JmxAttributeValidationException.checkNotNull(dependentModuleReadOnlyON,
72                 "is null, " + "expected dependency implementing "
73                         + expectedServiceInterface, jmxAttribute);
74
75         // check that objectName belongs to this transaction - this should be
76         // stripped
77         // in DynamicWritableWrapper
78         boolean hasTransaction = ObjectNameUtil
79                 .getTransactionName(dependentModuleReadOnlyON) != null;
80         JmxAttributeValidationException.checkCondition(
81                 hasTransaction == false,
82                 format("ObjectName should not contain "
83                         + "transaction name. %s set to %s. ", jmxAttribute,
84                         dependentModuleReadOnlyON), jmxAttribute);
85
86         ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentModuleReadOnlyON, ObjectNameUtil
87                 .TYPE_MODULE);
88
89         ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
90
91         boolean implementsSI = foundFactory
92                 .isModuleImplementingServiceInterface(expectedServiceInterface);
93         if (implementsSI == false) {
94             String message = format(
95                     "Found module factory does not expose expected service interface. "
96                             + "Module name is %s : %s, expected service interface %s, dependent module ON %s , "
97                             + "attribute %s",
98                     foundFactory.getImplementationName(), foundFactory,
99                     expectedServiceInterface, dependentModuleReadOnlyON,
100                     jmxAttribute);
101             throw new JmxAttributeValidationException(message, jmxAttribute);
102         }
103         synchronized (this) {
104             dependencies.add(moduleIdentifier);
105         }
106     }
107
108     @Override
109     public void validateDependency(
110             Class<? extends AbstractServiceInterface> expectedServiceInterface,
111             ObjectName objectName, String attributeNameForErrorReporting) {
112         validateDependency(expectedServiceInterface, objectName,
113                 new JmxAttribute(attributeNameForErrorReporting));
114     }
115
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentON,
121             JmxAttribute jmxAttribute) {
122         if (expectedType == null || dependentON == null || jmxAttribute == null) {
123             throw new IllegalArgumentException(format(
124                     "Null parameters not allowed, got {} {} {}", expectedType,
125                     dependentON, jmxAttribute));
126         }
127
128         transactionStatus.checkCommitStarted();
129         transactionStatus.checkNotCommitted();
130
131         ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
132                 dependentON, ObjectNameUtil.TYPE_MODULE);
133         Module module = modulesHolder.findModule(dependentModuleIdentifier,
134                 jmxAttribute);
135         synchronized (this) {
136             dependencies.add(dependentModuleIdentifier);
137         }
138         AutoCloseable instance = module.getInstance();
139         if (instance == null) {
140             String message = format(
141                     "Error while %s resolving instance %s. getInstance() returned null. "
142                             + "Expected type %s , attribute %s", name,
143                     dependentModuleIdentifier, expectedType, jmxAttribute);
144             throw new JmxAttributeValidationException(message, jmxAttribute);
145         }
146         try {
147             T result = expectedType.cast(instance);
148             return result;
149         } catch (ClassCastException e) {
150             String message = format(
151                     "Instance cannot be cast to expected type. Instance class is %s , "
152                             + "expected type %s , attribute %s",
153                     instance.getClass(), expectedType, jmxAttribute);
154             throw new JmxAttributeValidationException(message, e, jmxAttribute);
155         }
156     }
157
158     @Deprecated
159     @Override
160     public <T> T resolveInstance(Class<T> expectedType, ObjectName objectName) {
161         return resolveInstance(expectedType, objectName, new JmxAttribute(
162                 "unknown attribute"));
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.getName());
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 }