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.yangtools.concepts.Identifiable;
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 Identifiable<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);
112 public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentON,
113 JmxAttribute jmxAttribute) {
114 if (expectedType == null || dependentON == null || jmxAttribute == null) {
115 throw new IllegalArgumentException(format(
116 "Null parameters not allowed, got {} {} {}", expectedType,
117 dependentON, jmxAttribute));
120 transactionStatus.checkCommitStarted();
121 transactionStatus.checkNotCommitted();
123 ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
124 dependentON, ObjectNameUtil.TYPE_MODULE);
125 Module module = modulesHolder.findModule(dependentModuleIdentifier,
127 synchronized (this) {
128 dependencies.add(dependentModuleIdentifier);
130 AutoCloseable instance = module.getInstance();
131 if (instance == null) {
132 String message = format(
133 "Error while %s resolving instance %s. getInstance() returned null. "
134 + "Expected type %s , attribute %s", name,
135 dependentModuleIdentifier, expectedType, jmxAttribute);
136 throw new JmxAttributeValidationException(message, jmxAttribute);
139 T result = expectedType.cast(instance);
141 } catch (ClassCastException e) {
142 String message = format(
143 "Instance cannot be cast to expected type. Instance class is %s , "
144 + "expected type %s , attribute %s",
145 instance.getClass(), expectedType, jmxAttribute);
146 throw new JmxAttributeValidationException(message, e, jmxAttribute);
151 public int compareTo(DependencyResolverImpl o) {
152 transactionStatus.checkCommitted();
153 return Integer.compare(getMaxDependencyDepth(),
154 o.getMaxDependencyDepth());
157 private Integer maxDependencyDepth;
159 int getMaxDependencyDepth() {
160 if (maxDependencyDepth == null) {
161 throw new IllegalStateException("Dependency depth was not computed");
163 return maxDependencyDepth;
166 public void countMaxDependencyDepth(DependencyResolverManager manager) {
167 transactionStatus.checkCommitted();
168 if (maxDependencyDepth == null) {
169 maxDependencyDepth = getMaxDepth(this, manager,
170 new LinkedHashSet<ModuleIdentifier>());
174 private static int getMaxDepth(DependencyResolverImpl impl,
175 DependencyResolverManager manager,
176 LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
178 LinkedHashSet<ModuleIdentifier> chainForDetectingCycles2 = new LinkedHashSet<>(
179 chainForDetectingCycles);
180 chainForDetectingCycles2.add(impl.getName());
181 for (ModuleIdentifier dependencyName : impl.dependencies) {
182 DependencyResolverImpl dependentDRI = manager
183 .getOrCreate(dependencyName);
184 if (chainForDetectingCycles2.contains(dependencyName)) {
185 throw new IllegalStateException(format(
186 "Cycle detected, {} contains {}",
187 chainForDetectingCycles2, dependencyName));
190 if (dependentDRI.maxDependencyDepth != null) {
191 subDepth = dependentDRI.maxDependencyDepth;
193 subDepth = getMaxDepth(dependentDRI, manager,
194 chainForDetectingCycles2);
195 dependentDRI.maxDependencyDepth = subDepth;
197 if (subDepth + 1 > maxDepth) {
198 maxDepth = subDepth + 1;
201 impl.maxDependencyDepth = maxDepth;
206 public ModuleIdentifier getIdentifier() {