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 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;
21 import javax.annotation.concurrent.GuardedBy;
22 import javax.management.ObjectName;
23 import java.util.HashSet;
24 import java.util.LinkedHashSet;
27 import static java.lang.String.format;
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.
34 final class DependencyResolverImpl implements DependencyResolver,
35 Comparable<DependencyResolverImpl> {
36 private final ModulesHolder modulesHolder;
37 private final ModuleIdentifier name;
38 private final TransactionStatus transactionStatus;
40 private final Set<ModuleIdentifier> dependencies = new HashSet<>();
41 private final ServiceReferenceReadableRegistry readableRegistry;
43 DependencyResolverImpl(ModuleIdentifier currentModule,
44 TransactionStatus transactionStatus, ModulesHolder modulesHolder,
45 ServiceReferenceReadableRegistry readableRegistry) {
47 this.name = currentModule;
48 this.transactionStatus = transactionStatus;
49 this.modulesHolder = modulesHolder;
50 this.readableRegistry = readableRegistry;
56 //TODO: check for cycles
58 public void validateDependency(
59 Class<? extends AbstractServiceInterface> expectedServiceInterface,
60 ObjectName dependentReadOnlyON, JmxAttribute jmxAttribute) {
62 transactionStatus.checkNotCommitted();
63 if (expectedServiceInterface == null) {
64 throw new NullPointerException(
65 "Parameter 'expectedServiceInterface' is null");
67 if (jmxAttribute == null)
68 throw new NullPointerException("Parameter 'jmxAttribute' is null");
70 JmxAttributeValidationException.checkNotNull(dependentReadOnlyON,
71 "is null, " + "expected dependency implementing "
72 + expectedServiceInterface, jmxAttribute);
76 // check that objectName belongs to this transaction - this should be
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);
87 dependentReadOnlyON = translateServiceRefIfPossible(dependentReadOnlyON);
89 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentReadOnlyON, ObjectNameUtil
92 ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
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 , "
101 foundFactory.getImplementationName(), foundFactory,
102 expectedServiceInterface, dependentReadOnlyON,
104 throw new JmxAttributeValidationException(message, jmxAttribute);
106 synchronized (this) {
107 dependencies.add(moduleIdentifier);
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));
119 return dependentReadOnlyON;
125 //TODO: check for cycles
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));
134 dependentReadOnlyON = translateServiceRefIfPossible(dependentReadOnlyON);
135 transactionStatus.checkCommitStarted();
136 transactionStatus.checkNotCommitted();
138 ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
139 dependentReadOnlyON, ObjectNameUtil.TYPE_MODULE);
140 Module module = modulesHolder.findModule(dependentModuleIdentifier,
142 synchronized (this) {
143 dependencies.add(dependentModuleIdentifier);
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);
154 T result = expectedType.cast(instance);
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);
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.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));
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;
221 public ModuleIdentifier getIdentifier() {