2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.config.manager.impl.dependencyresolver;
10 import static java.lang.String.format;
12 import java.util.HashSet;
13 import java.util.LinkedHashSet;
16 import javax.annotation.concurrent.GuardedBy;
17 import javax.management.ObjectName;
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;
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.
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;
41 private final Set<ModuleIdentifier> dependencies = new HashSet<>();
43 DependencyResolverImpl(ModuleIdentifier currentModule,
44 TransactionStatus transactionStatus, ModulesHolder modulesHolder) {
45 this.name = currentModule;
46 this.transactionStatus = transactionStatus;
47 this.modulesHolder = modulesHolder;
51 public ModuleIdentifier getName() {
59 public void validateDependency(
60 Class<? extends AbstractServiceInterface> expectedServiceInterface,
61 ObjectName dependentModuleReadOnlyON, JmxAttribute jmxAttribute) {
63 transactionStatus.checkNotCommitted();
64 if (expectedServiceInterface == null) {
65 throw new NullPointerException(
66 "Parameter 'expectedServiceInterface' is null");
68 if (jmxAttribute == null)
69 throw new NullPointerException("Parameter 'jmxAttribute' is null");
71 JmxAttributeValidationException.checkNotNull(dependentModuleReadOnlyON,
72 "is null, " + "expected dependency implementing "
73 + expectedServiceInterface, jmxAttribute);
75 // check that objectName belongs to this transaction - this should be
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);
86 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentModuleReadOnlyON, ObjectNameUtil
89 ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
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 , "
98 foundFactory.getImplementationName(), foundFactory,
99 expectedServiceInterface, dependentModuleReadOnlyON,
101 throw new JmxAttributeValidationException(message, jmxAttribute);
103 synchronized (this) {
104 dependencies.add(moduleIdentifier);
109 public void validateDependency(
110 Class<? extends AbstractServiceInterface> expectedServiceInterface,
111 ObjectName objectName, String attributeNameForErrorReporting) {
112 validateDependency(expectedServiceInterface, objectName,
113 new JmxAttribute(attributeNameForErrorReporting));
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));
128 transactionStatus.checkCommitStarted();
129 transactionStatus.checkNotCommitted();
131 ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
132 dependentON, ObjectNameUtil.TYPE_MODULE);
133 Module module = modulesHolder.findModule(dependentModuleIdentifier,
135 synchronized (this) {
136 dependencies.add(dependentModuleIdentifier);
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);
147 T result = expectedType.cast(instance);
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);
160 public <T> T resolveInstance(Class<T> expectedType, ObjectName objectName) {
161 return resolveInstance(expectedType, objectName, new JmxAttribute(
162 "unknown attribute"));
166 public int compareTo(DependencyResolverImpl o) {
167 transactionStatus.checkCommitted();
168 return Integer.compare(getMaxDependencyDepth(),
169 o.getMaxDependencyDepth());
172 private Integer maxDependencyDepth;
174 int getMaxDependencyDepth() {
175 if (maxDependencyDepth == null) {
176 throw new IllegalStateException("Dependency depth was not computed");
178 return maxDependencyDepth;
181 public void countMaxDependencyDepth(DependencyResolverManager manager) {
182 transactionStatus.checkCommitted();
183 if (maxDependencyDepth == null) {
184 maxDependencyDepth = getMaxDepth(this, manager,
185 new LinkedHashSet<ModuleIdentifier>());
189 private static int getMaxDepth(DependencyResolverImpl impl,
190 DependencyResolverManager manager,
191 LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
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));
205 if (dependentDRI.maxDependencyDepth != null) {
206 subDepth = dependentDRI.maxDependencyDepth;
208 subDepth = getMaxDepth(dependentDRI, manager,
209 chainForDetectingCycles2);
210 dependentDRI.maxDependencyDepth = subDepth;
212 if (subDepth + 1 > maxDepth) {
213 maxDepth = subDepth + 1;
216 impl.maxDependencyDepth = maxDepth;