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.IdentityAttributeRef;
12 import org.opendaylight.controller.config.api.JmxAttribute;
13 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
14 import org.opendaylight.controller.config.api.ModuleIdentifier;
15 import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
16 import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
17 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
18 import org.opendaylight.controller.config.manager.impl.TransactionStatus;
19 import org.opendaylight.controller.config.spi.Module;
20 import org.opendaylight.controller.config.spi.ModuleFactory;
21 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
22 import org.opendaylight.yangtools.yang.common.QName;
23 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
24 import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
28 import javax.annotation.concurrent.GuardedBy;
29 import javax.management.ObjectName;
30 import java.util.HashSet;
31 import java.util.LinkedHashSet;
34 import static java.lang.String.format;
37 * Protect {@link org.opendaylight.controller.config.spi.Module#getInstance()}
38 * by creating proxy that would throw exception if those methods are called
39 * during validation. Tracks dependencies for ordering purposes.
41 final class DependencyResolverImpl implements DependencyResolver,
42 Comparable<DependencyResolverImpl> {
43 private static final Logger logger = LoggerFactory.getLogger(DependencyResolverImpl.class);
45 private final ModulesHolder modulesHolder;
46 private final ModuleIdentifier name;
47 private final TransactionStatus transactionStatus;
49 private final Set<ModuleIdentifier> dependencies = new HashSet<>();
50 private final ServiceReferenceReadableRegistry readableRegistry;
51 private final CodecRegistry codecRegistry;
53 DependencyResolverImpl(ModuleIdentifier currentModule,
54 TransactionStatus transactionStatus, ModulesHolder modulesHolder,
55 ServiceReferenceReadableRegistry readableRegistry, CodecRegistry codecRegistry) {
56 this.codecRegistry = codecRegistry;
57 this.name = currentModule;
58 this.transactionStatus = transactionStatus;
59 this.modulesHolder = modulesHolder;
60 this.readableRegistry = readableRegistry;
66 //TODO: check for cycles
68 public void validateDependency(
69 Class<? extends AbstractServiceInterface> expectedServiceInterface,
70 ObjectName dependentReadOnlyON, JmxAttribute jmxAttribute) {
72 transactionStatus.checkNotCommitted();
73 if (expectedServiceInterface == null) {
74 throw new NullPointerException(
75 "Parameter 'expectedServiceInterface' is null");
77 if (jmxAttribute == null)
78 throw new NullPointerException("Parameter 'jmxAttribute' is null");
80 JmxAttributeValidationException.checkNotNull(dependentReadOnlyON,
81 "is null, expected dependency implementing "
82 + expectedServiceInterface, jmxAttribute);
86 // check that objectName belongs to this transaction - this should be
88 // in DynamicWritableWrapper
89 boolean hasTransaction = ObjectNameUtil
90 .getTransactionName(dependentReadOnlyON) != null;
91 JmxAttributeValidationException.checkCondition(
92 hasTransaction == false,
93 format("ObjectName should not contain "
94 + "transaction name. %s set to %s. ", jmxAttribute,
95 dependentReadOnlyON), jmxAttribute);
97 dependentReadOnlyON = translateServiceRefIfPossible(dependentReadOnlyON);
99 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentReadOnlyON, ObjectNameUtil
102 ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
104 boolean implementsSI = foundFactory
105 .isModuleImplementingServiceInterface(expectedServiceInterface);
106 if (implementsSI == false) {
107 String message = format(
108 "Found module factory does not expose expected service interface. "
109 + "Module name is %s : %s, expected service interface %s, dependent module ON %s , "
111 foundFactory.getImplementationName(), foundFactory,
112 expectedServiceInterface, dependentReadOnlyON,
114 throw new JmxAttributeValidationException(message, jmxAttribute);
116 synchronized (this) {
117 dependencies.add(moduleIdentifier);
121 // transalate from serviceref to module ON
122 private ObjectName translateServiceRefIfPossible(ObjectName dependentReadOnlyON) {
123 if (ObjectNameUtil.isServiceReference(dependentReadOnlyON)) {
124 String serviceQName = ObjectNameUtil.getServiceQName(dependentReadOnlyON);
125 String refName = ObjectNameUtil.getReferenceName(dependentReadOnlyON);
126 dependentReadOnlyON = ObjectNameUtil.withoutTransactionName( // strip again of transaction name
127 readableRegistry.lookupConfigBeanByServiceInterfaceName(serviceQName, refName));
129 return dependentReadOnlyON;
135 //TODO: check for cycles
137 public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentReadOnlyON,
138 JmxAttribute jmxAttribute) {
139 if (expectedType == null || dependentReadOnlyON == null || jmxAttribute == null) {
140 throw new IllegalArgumentException(format(
141 "Null parameters not allowed, got %s %s %s", expectedType,
142 dependentReadOnlyON, jmxAttribute));
144 dependentReadOnlyON = translateServiceRefIfPossible(dependentReadOnlyON);
145 transactionStatus.checkCommitStarted();
146 transactionStatus.checkNotCommitted();
148 ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
149 dependentReadOnlyON, ObjectNameUtil.TYPE_MODULE);
150 Module module = modulesHolder.findModule(dependentModuleIdentifier,
152 synchronized (this) {
153 dependencies.add(dependentModuleIdentifier);
155 AutoCloseable instance = module.getInstance();
156 if (instance == null) {
157 String message = format(
158 "Error while %s resolving instance %s. getInstance() returned null. "
159 + "Expected type %s , attribute %s", name,
160 dependentModuleIdentifier, expectedType, jmxAttribute);
161 throw new JmxAttributeValidationException(message, jmxAttribute);
164 T result = expectedType.cast(instance);
166 } catch (ClassCastException e) {
167 String message = format(
168 "Instance cannot be cast to expected type. Instance class is %s , "
169 + "expected type %s , attribute %s",
170 instance.getClass(), expectedType, jmxAttribute);
171 throw new JmxAttributeValidationException(message, e, jmxAttribute);
176 public <T extends BaseIdentity> Class<? extends T> resolveIdentity(IdentityAttributeRef identityRef, Class<T> expectedBaseClass) {
177 final QName qName = QName.create(identityRef.getqNameOfIdentity());
178 IdentityCodec<?> identityCodec = codecRegistry.getIdentityCodec();
179 Class<? extends BaseIdentity> deserialized = identityCodec.deserialize(qName);
180 if (deserialized == null) {
181 throw new RuntimeException("Unable to retrieve identity class for " + qName + ", null response from "
184 if (expectedBaseClass.isAssignableFrom(deserialized)) {
185 return (Class<T>) deserialized;
187 logger.error("Cannot resolve class of identity {} : deserialized class {} is not a subclass of {}.",
188 identityRef, deserialized, expectedBaseClass);
189 throw new IllegalArgumentException("Deserialized identity " + deserialized + " cannot be cast to " + expectedBaseClass);
194 public <T extends BaseIdentity> void validateIdentity(IdentityAttributeRef identityRef, Class<T> expectedBaseClass, JmxAttribute jmxAttribute) {
196 resolveIdentity(identityRef, expectedBaseClass);
197 } catch(Exception e) {
198 throw JmxAttributeValidationException.wrap(e, jmxAttribute);
203 public int compareTo(DependencyResolverImpl o) {
204 transactionStatus.checkCommitted();
205 return Integer.compare(getMaxDependencyDepth(),
206 o.getMaxDependencyDepth());
209 private Integer maxDependencyDepth;
211 int getMaxDependencyDepth() {
212 if (maxDependencyDepth == null) {
213 throw new IllegalStateException("Dependency depth was not computed");
215 return maxDependencyDepth;
218 public void countMaxDependencyDepth(DependencyResolverManager manager) {
219 transactionStatus.checkCommitted();
220 if (maxDependencyDepth == null) {
221 maxDependencyDepth = getMaxDepth(this, manager,
222 new LinkedHashSet<ModuleIdentifier>());
226 private static int getMaxDepth(DependencyResolverImpl impl,
227 DependencyResolverManager manager,
228 LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
230 LinkedHashSet<ModuleIdentifier> chainForDetectingCycles2 = new LinkedHashSet<>(
231 chainForDetectingCycles);
232 chainForDetectingCycles2.add(impl.getIdentifier());
233 for (ModuleIdentifier dependencyName : impl.dependencies) {
234 DependencyResolverImpl dependentDRI = manager
235 .getOrCreate(dependencyName);
236 if (chainForDetectingCycles2.contains(dependencyName)) {
237 throw new IllegalStateException(format(
238 "Cycle detected, %s contains %s",
239 chainForDetectingCycles2, dependencyName));
242 if (dependentDRI.maxDependencyDepth != null) {
243 subDepth = dependentDRI.maxDependencyDepth;
245 subDepth = getMaxDepth(dependentDRI, manager,
246 chainForDetectingCycles2);
247 dependentDRI.maxDependencyDepth = subDepth;
249 if (subDepth + 1 > maxDepth) {
250 maxDepth = subDepth + 1;
253 impl.maxDependencyDepth = maxDepth;
258 public ModuleIdentifier getIdentifier() {