Merge "removed parent dependency on pax-exam-container-native"
[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 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.annotations.AbstractServiceInterface;
15 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
16 import org.opendaylight.controller.config.manager.impl.TransactionStatus;
17 import org.opendaylight.controller.config.spi.Module;
18 import org.opendaylight.controller.config.spi.ModuleFactory;
19
20 import javax.annotation.concurrent.GuardedBy;
21 import javax.management.ObjectName;
22 import java.util.HashSet;
23 import java.util.LinkedHashSet;
24 import java.util.Set;
25
26 import static java.lang.String.format;
27
28 /**
29  * Protect {@link org.opendaylight.controller.config.spi.Module#getInstance()}
30  * by creating proxy that would throw exception if those methods are called
31  * during validation. Tracks dependencies for ordering purposes.
32  */
33 final class DependencyResolverImpl implements DependencyResolver,
34        Comparable<DependencyResolverImpl> {
35     private final ModulesHolder modulesHolder;
36     private final ModuleIdentifier name;
37     private final TransactionStatus transactionStatus;
38     @GuardedBy("this")
39     private final Set<ModuleIdentifier> dependencies = new HashSet<>();
40
41     DependencyResolverImpl(ModuleIdentifier currentModule,
42             TransactionStatus transactionStatus, ModulesHolder modulesHolder) {
43         this.name = currentModule;
44         this.transactionStatus = transactionStatus;
45         this.modulesHolder = modulesHolder;
46     }
47
48     /**
49      * {@inheritDoc}
50      */
51     //TODO: check for cycles
52     @Override
53     public void validateDependency(
54             Class<? extends AbstractServiceInterface> expectedServiceInterface,
55             ObjectName dependentModuleReadOnlyON, JmxAttribute jmxAttribute) {
56
57         transactionStatus.checkNotCommitted();
58         if (expectedServiceInterface == null) {
59             throw new NullPointerException(
60                     "Parameter 'expectedServiceInterface' is null");
61         }
62         if (jmxAttribute == null)
63             throw new NullPointerException("Parameter 'jmxAttribute' is null");
64
65         JmxAttributeValidationException.checkNotNull(dependentModuleReadOnlyON,
66                 "is null, " + "expected dependency implementing "
67                         + expectedServiceInterface, jmxAttribute);
68
69         // check that objectName belongs to this transaction - this should be
70         // stripped
71         // in DynamicWritableWrapper
72         boolean hasTransaction = ObjectNameUtil
73                 .getTransactionName(dependentModuleReadOnlyON) != null;
74         JmxAttributeValidationException.checkCondition(
75                 hasTransaction == false,
76                 format("ObjectName should not contain "
77                         + "transaction name. %s set to %s. ", jmxAttribute,
78                         dependentModuleReadOnlyON), jmxAttribute);
79
80         ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentModuleReadOnlyON, ObjectNameUtil
81                 .TYPE_MODULE);
82
83         ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
84
85         boolean implementsSI = foundFactory
86                 .isModuleImplementingServiceInterface(expectedServiceInterface);
87         if (implementsSI == false) {
88             String message = format(
89                     "Found module factory does not expose expected service interface. "
90                             + "Module name is %s : %s, expected service interface %s, dependent module ON %s , "
91                             + "attribute %s",
92                     foundFactory.getImplementationName(), foundFactory,
93                     expectedServiceInterface, dependentModuleReadOnlyON,
94                     jmxAttribute);
95             throw new JmxAttributeValidationException(message, jmxAttribute);
96         }
97         synchronized (this) {
98             dependencies.add(moduleIdentifier);
99         }
100     }
101
102     /**
103      * {@inheritDoc}
104      */
105     //TODO: check for cycles
106     @Override
107     public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentON,
108             JmxAttribute jmxAttribute) {
109         if (expectedType == null || dependentON == null || jmxAttribute == null) {
110             throw new IllegalArgumentException(format(
111                     "Null parameters not allowed, got {} {} {}", expectedType,
112                     dependentON, jmxAttribute));
113         }
114
115         transactionStatus.checkCommitStarted();
116         transactionStatus.checkNotCommitted();
117
118         ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
119                 dependentON, ObjectNameUtil.TYPE_MODULE);
120         Module module = modulesHolder.findModule(dependentModuleIdentifier,
121                 jmxAttribute);
122         synchronized (this) {
123             dependencies.add(dependentModuleIdentifier);
124         }
125         AutoCloseable instance = module.getInstance();
126         if (instance == null) {
127             String message = format(
128                     "Error while %s resolving instance %s. getInstance() returned null. "
129                             + "Expected type %s , attribute %s", name,
130                     dependentModuleIdentifier, expectedType, jmxAttribute);
131             throw new JmxAttributeValidationException(message, jmxAttribute);
132         }
133         try {
134             T result = expectedType.cast(instance);
135             return result;
136         } catch (ClassCastException e) {
137             String message = format(
138                     "Instance cannot be cast to expected type. Instance class is %s , "
139                             + "expected type %s , attribute %s",
140                     instance.getClass(), expectedType, jmxAttribute);
141             throw new JmxAttributeValidationException(message, e, jmxAttribute);
142         }
143     }
144
145     @Override
146     public int compareTo(DependencyResolverImpl o) {
147         transactionStatus.checkCommitted();
148         return Integer.compare(getMaxDependencyDepth(),
149                 o.getMaxDependencyDepth());
150     }
151
152     private Integer maxDependencyDepth;
153
154     int getMaxDependencyDepth() {
155         if (maxDependencyDepth == null) {
156             throw new IllegalStateException("Dependency depth was not computed");
157         }
158         return maxDependencyDepth;
159     }
160
161     public void countMaxDependencyDepth(DependencyResolverManager manager) {
162         transactionStatus.checkCommitted();
163         if (maxDependencyDepth == null) {
164             maxDependencyDepth = getMaxDepth(this, manager,
165                     new LinkedHashSet<ModuleIdentifier>());
166         }
167     }
168
169     private static int getMaxDepth(DependencyResolverImpl impl,
170             DependencyResolverManager manager,
171             LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
172         int maxDepth = 0;
173         LinkedHashSet<ModuleIdentifier> chainForDetectingCycles2 = new LinkedHashSet<>(
174                 chainForDetectingCycles);
175         chainForDetectingCycles2.add(impl.getIdentifier());
176         for (ModuleIdentifier dependencyName : impl.dependencies) {
177             DependencyResolverImpl dependentDRI = manager
178                     .getOrCreate(dependencyName);
179             if (chainForDetectingCycles2.contains(dependencyName)) {
180                 throw new IllegalStateException(format(
181                         "Cycle detected, {} contains {}",
182                         chainForDetectingCycles2, dependencyName));
183             }
184             int subDepth;
185             if (dependentDRI.maxDependencyDepth != null) {
186                 subDepth = dependentDRI.maxDependencyDepth;
187             } else {
188                 subDepth = getMaxDepth(dependentDRI, manager,
189                         chainForDetectingCycles2);
190                 dependentDRI.maxDependencyDepth = subDepth;
191             }
192             if (subDepth + 1 > maxDepth) {
193                 maxDepth = subDepth + 1;
194             }
195         }
196         impl.maxDependencyDepth = maxDepth;
197         return maxDepth;
198     }
199
200     @Override
201     public ModuleIdentifier getIdentifier() {
202         return name;
203     }
204 }