Merge "Modifying user roles via GUI"
[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     @Override
52     public void validateDependency(
53             Class<? extends AbstractServiceInterface> expectedServiceInterface,
54             ObjectName dependentModuleReadOnlyON, JmxAttribute jmxAttribute) {
55
56         transactionStatus.checkNotCommitted();
57         if (expectedServiceInterface == null) {
58             throw new NullPointerException(
59                     "Parameter 'expectedServiceInterface' is null");
60         }
61         if (jmxAttribute == null)
62             throw new NullPointerException("Parameter 'jmxAttribute' is null");
63
64         JmxAttributeValidationException.checkNotNull(dependentModuleReadOnlyON,
65                 "is null, " + "expected dependency implementing "
66                         + expectedServiceInterface, jmxAttribute);
67
68         // check that objectName belongs to this transaction - this should be
69         // stripped
70         // in DynamicWritableWrapper
71         boolean hasTransaction = ObjectNameUtil
72                 .getTransactionName(dependentModuleReadOnlyON) != null;
73         JmxAttributeValidationException.checkCondition(
74                 hasTransaction == false,
75                 format("ObjectName should not contain "
76                         + "transaction name. %s set to %s. ", jmxAttribute,
77                         dependentModuleReadOnlyON), jmxAttribute);
78
79         ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentModuleReadOnlyON, ObjectNameUtil
80                 .TYPE_MODULE);
81
82         ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
83
84         boolean implementsSI = foundFactory
85                 .isModuleImplementingServiceInterface(expectedServiceInterface);
86         if (implementsSI == false) {
87             String message = format(
88                     "Found module factory does not expose expected service interface. "
89                             + "Module name is %s : %s, expected service interface %s, dependent module ON %s , "
90                             + "attribute %s",
91                     foundFactory.getImplementationName(), foundFactory,
92                     expectedServiceInterface, dependentModuleReadOnlyON,
93                     jmxAttribute);
94             throw new JmxAttributeValidationException(message, jmxAttribute);
95         }
96         synchronized (this) {
97             dependencies.add(moduleIdentifier);
98         }
99     }
100
101     /**
102      * {@inheritDoc}
103      */
104     @Override
105     public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentON,
106             JmxAttribute jmxAttribute) {
107         if (expectedType == null || dependentON == null || jmxAttribute == null) {
108             throw new IllegalArgumentException(format(
109                     "Null parameters not allowed, got {} {} {}", expectedType,
110                     dependentON, jmxAttribute));
111         }
112
113         transactionStatus.checkCommitStarted();
114         transactionStatus.checkNotCommitted();
115
116         ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
117                 dependentON, ObjectNameUtil.TYPE_MODULE);
118         Module module = modulesHolder.findModule(dependentModuleIdentifier,
119                 jmxAttribute);
120         synchronized (this) {
121             dependencies.add(dependentModuleIdentifier);
122         }
123         AutoCloseable instance = module.getInstance();
124         if (instance == null) {
125             String message = format(
126                     "Error while %s resolving instance %s. getInstance() returned null. "
127                             + "Expected type %s , attribute %s", name,
128                     dependentModuleIdentifier, expectedType, jmxAttribute);
129             throw new JmxAttributeValidationException(message, jmxAttribute);
130         }
131         try {
132             T result = expectedType.cast(instance);
133             return result;
134         } catch (ClassCastException e) {
135             String message = format(
136                     "Instance cannot be cast to expected type. Instance class is %s , "
137                             + "expected type %s , attribute %s",
138                     instance.getClass(), expectedType, jmxAttribute);
139             throw new JmxAttributeValidationException(message, e, jmxAttribute);
140         }
141     }
142
143     @Override
144     public int compareTo(DependencyResolverImpl o) {
145         transactionStatus.checkCommitted();
146         return Integer.compare(getMaxDependencyDepth(),
147                 o.getMaxDependencyDepth());
148     }
149
150     private Integer maxDependencyDepth;
151
152     int getMaxDependencyDepth() {
153         if (maxDependencyDepth == null) {
154             throw new IllegalStateException("Dependency depth was not computed");
155         }
156         return maxDependencyDepth;
157     }
158
159     public void countMaxDependencyDepth(DependencyResolverManager manager) {
160         transactionStatus.checkCommitted();
161         if (maxDependencyDepth == null) {
162             maxDependencyDepth = getMaxDepth(this, manager,
163                     new LinkedHashSet<ModuleIdentifier>());
164         }
165     }
166
167     private static int getMaxDepth(DependencyResolverImpl impl,
168             DependencyResolverManager manager,
169             LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
170         int maxDepth = 0;
171         LinkedHashSet<ModuleIdentifier> chainForDetectingCycles2 = new LinkedHashSet<>(
172                 chainForDetectingCycles);
173         chainForDetectingCycles2.add(impl.getIdentifier());
174         for (ModuleIdentifier dependencyName : impl.dependencies) {
175             DependencyResolverImpl dependentDRI = manager
176                     .getOrCreate(dependencyName);
177             if (chainForDetectingCycles2.contains(dependencyName)) {
178                 throw new IllegalStateException(format(
179                         "Cycle detected, {} contains {}",
180                         chainForDetectingCycles2, dependencyName));
181             }
182             int subDepth;
183             if (dependentDRI.maxDependencyDepth != null) {
184                 subDepth = dependentDRI.maxDependencyDepth;
185             } else {
186                 subDepth = getMaxDepth(dependentDRI, manager,
187                         chainForDetectingCycles2);
188                 dependentDRI.maxDependencyDepth = subDepth;
189             }
190             if (subDepth + 1 > maxDepth) {
191                 maxDepth = subDepth + 1;
192             }
193         }
194         impl.maxDependencyDepth = maxDepth;
195         return maxDepth;
196     }
197
198     @Override
199     public ModuleIdentifier getIdentifier() {
200         return name;
201     }
202 }