*/
package org.opendaylight.controller.config.spi;
-import javax.annotation.concurrent.NotThreadSafe;
-
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.yangtools.concepts.Identifiable;
+import javax.annotation.concurrent.NotThreadSafe;
+
/**
* Represents one service that is to be configured. These methods need to be
* ConfigBeans.
* <p>
* In order to guide dependency resolution, the setter method should be
- * annotated with {@link RequireInterface}.
+ * annotated with {@link org.opendaylight.controller.config.api.annotations.RequireInterface}.
* </p>
* <p>
* Thread safety note: implementations of this interface are not required to be
* Returns 'live' object that was configured using this object. It is
* allowed to call this method only after all ConfigBeans were validated. In
* this method new resources might be opened or old instance might be
- * modified. Note that when obtaining dependent Module using
- * {@link org.opendaylight.controller.config.api.DependencyResolver#validateDependency(Class, javax.management.ObjectName, String)}
- * a proxy will be created that will disallow calling this method before
- * second commit phase begins.
+ * modified. This method must be implemented so that it returns same
+ * result for a single transaction. Since Module is created per transaction
+ * this means that it must be safe to cache result of first call.
+ *
*
* @return closeable instance: After bundle update the factory might be able
* to copy old configuration into new one without being able to cast
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
import javax.annotation.concurrent.Immutable;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.DestroyedModule;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
/**
* Structure obtained during first phase commit, contains destroyed modules from
*/
package org.opendaylight.controller.config.manager.impl;
+import com.google.common.collect.Maps;
import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.api.jmx.CommitStatus;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.DestroyedModule;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
MBeanServer configMBeanServer,
BaseJMXRegistrator baseJMXRegistrator, CodecRegistry codecRegistry) {
this.resolver = resolver;
- beanToOsgiServiceManager = new BeanToOsgiServiceManager();
+ this.beanToOsgiServiceManager = new BeanToOsgiServiceManager();
this.configMBeanServer = configMBeanServer;
this.baseJMXRegistrator = baseJMXRegistrator;
this.codecRegistry = codecRegistry;
- registryMBeanServer = MBeanServerFactory
+ this.registryMBeanServer = MBeanServerFactory
.createMBeanServer("ConfigRegistry" + configMBeanServer.getDefaultDomain());
- transactionsMBeanServer = MBeanServerFactory
+ this.transactionsMBeanServer = MBeanServerFactory
.createMBeanServer("ConfigTransactions" + configMBeanServer.getDefaultDomain());
}
throw new IllegalStateException(e);
}
transactionController.copyExistingModulesAndProcessFactoryDiff(currentConfig.getEntries(), lastListOfFactories);
- transactionsHolder.add(transactionName, transactionController);
+ transactionsHolder.add(transactionName, transactionController, txLookupRegistry);
return transactionController;
}
logger.trace("About to commit {}. Current parentVersion: {}, versionCounter {}", transactionName, version, versionCounter);
// find ConfigTransactionController
- Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder.getCurrentTransactions();
- ConfigTransactionControllerInternal configTransactionController = transactions.get(transactionName);
- if (configTransactionController == null) {
+ Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = transactionsHolder.getCurrentTransactions();
+ Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry> configTransactionControllerEntry = transactions.get(transactionName);
+ if (configTransactionControllerEntry == null) {
throw new IllegalArgumentException(String.format(
"Transaction with name '%s' not found", transactionName));
}
+ ConfigTransactionControllerInternal configTransactionController = configTransactionControllerEntry.getKey();
// check optimistic lock
if (version != configTransactionController.getParentVersion()) {
throw new ConflictingVersionException(
lastListOfFactories = Collections.unmodifiableList(configTransactionController.getCurrentlyRegisteredFactories());
// non recoverable from here:
try {
- return secondPhaseCommit(
- configTransactionController, commitInfo);
+ return secondPhaseCommit(configTransactionController, commitInfo, configTransactionControllerEntry.getValue());
} catch (Throwable t) { // some libs throw Errors: e.g.
// javax.xml.ws.spi.FactoryFinder$ConfigurationError
isHealthy = false;
} else if (t instanceof Error) {
throw (Error) t;
} else {
- throw new IllegalStateException(t);
+ throw new RuntimeException(t);
}
}
}
private CommitStatus secondPhaseCommit(ConfigTransactionControllerInternal configTransactionController,
- CommitInfo commitInfo) {
+ CommitInfo commitInfo, ConfigTransactionLookupRegistry txLookupRegistry) {
// close instances which were destroyed by the user, including
// (hopefully) runtime beans
.getRuntimeBeanRegistrator();
}
// set runtime jmx registrator if required
- Module module = entry.getModule();
+ Module module = entry.getProxiedModule();
if (module instanceof RuntimeBeanRegistratorAwareModule) {
((RuntimeBeanRegistratorAwareModule) module)
.setRuntimeBeanRegistrator(runtimeBeanRegistrator);
}
// can register runtime beans
- List<ModuleIdentifier> orderedModuleIdentifiers = configTransactionController
- .secondPhaseCommit();
+ List<ModuleIdentifier> orderedModuleIdentifiers = configTransactionController.secondPhaseCommit();
+ txLookupRegistry.close();
+ configTransactionController.close();
// copy configuration to read only mode
List<ObjectName> newInstances = new LinkedList<>();
throw new NullPointerException("Module not found "
+ moduleIdentifier);
}
- Module module = entry.getModule();
+
ObjectName primaryReadOnlyON = ObjectNameUtil
.createReadOnlyModuleON(moduleIdentifier);
.createModuleJMXRegistrator();
OsgiRegistration osgiRegistration = null;
+ AutoCloseable instance = entry.getProxiedModule().getInstance();
if (entry.hasOldModule()) {
ModuleInternalInfo oldInternalInfo = entry.getOldInternalInfo();
DynamicReadableWrapper oldReadableConfigBean = oldInternalInfo.getReadableModule();
currentConfig.remove(entry.getIdentifier());
// test if old instance == new instance
- if (oldReadableConfigBean.getInstance().equals(module.getInstance())) {
+ if (oldReadableConfigBean.getInstance().equals(instance)) {
// reused old instance:
// wrap in readable dynamic mbean
reusedInstances.add(primaryReadOnlyON);
// wrap in readable dynamic mbean
newInstances.add(primaryReadOnlyON);
}
+ Module realModule = entry.getRealModule();
DynamicReadableWrapper newReadableConfigBean = new DynamicReadableWrapper(
- module, module.getInstance(), moduleIdentifier,
+ realModule, instance, moduleIdentifier,
registryMBeanServer, configMBeanServer);
// register to JMX
if (moduleFactory != null) {
BundleContext bc = configTransactionController.
getModuleFactoryBundleContext(moduleFactory.getImplementationName());
- osgiRegistration = beanToOsgiServiceManager.registerToOsgi(module.getClass(),
+ osgiRegistration = beanToOsgiServiceManager.registerToOsgi(realModule.getClass(),
newReadableConfigBean.getInstance(), entry.getIdentifier(), bc);
} else {
throw new NullPointerException(entry.getIdentifier().getFactoryName() + " ModuleFactory not found.");
runtimeBeanRegistrator, newModuleJMXRegistrator,
orderingIdx, entry.isDefaultBean());
- newConfigEntries.put(module, newInfo);
+ newConfigEntries.put(realModule, newInfo);
orderingIdx++;
}
currentConfig.addAll(newConfigEntries.values());
version = configTransactionController.getVersion();
// switch readable Service Reference Registry
- readableSRRegistry.close();
- readableSRRegistry = ServiceReferenceRegistryImpl.createSRReadableRegistry(
+ this.readableSRRegistry.close();
+ this.readableSRRegistry = ServiceReferenceRegistryImpl.createSRReadableRegistry(
configTransactionController.getWritableRegistry(), this, baseJMXRegistrator);
return new CommitStatus(newInstances, reusedInstances,
*/
@Override
public synchronized List<ObjectName> getOpenConfigs() {
- Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder
+ Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = transactionsHolder
.getCurrentTransactions();
List<ObjectName> result = new ArrayList<>(transactions.size());
- for (ConfigTransactionControllerInternal configTransactionController : transactions
+ for (Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry> configTransactionControllerEntry : transactions
.values()) {
- result.add(configTransactionController.getControllerObjectName());
+ result.add(configTransactionControllerEntry.getKey().getControllerObjectName());
}
return result;
}
@Override
public synchronized void close() {
// abort transactions
- Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder
+ Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = transactionsHolder
.getCurrentTransactions();
- for (ConfigTransactionControllerInternal configTransactionController : transactions
+ for (Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry> configTransactionControllerEntry : transactions
.values()) {
+
+ ConfigTransactionControllerInternal configTransactionController = configTransactionControllerEntry.getKey();
try {
+ configTransactionControllerEntry.getValue().close();
configTransactionController.abortConfig();
} catch (RuntimeException e) {
logger.warn("Ignoring exception while aborting {}",
* every time current transactions are requested.
*/
@GuardedBy("ConfigRegistryImpl.this")
- private final Map<String /* transactionName */, ConfigTransactionControllerInternal> transactions = new HashMap<>();
+ private final Map<String /* transactionName */,
+ Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions = new HashMap<>();
/**
* Can only be called from within synchronized method.
*/
public void add(String transactionName,
- ConfigTransactionControllerInternal transactionController) {
+ ConfigTransactionControllerInternal transactionController, ConfigTransactionLookupRegistry txLookupRegistry) {
Object oldValue = transactions.put(transactionName,
- transactionController);
+ Maps.immutableEntry(transactionController, txLookupRegistry));
if (oldValue != null) {
throw new IllegalStateException(
"Error: two transactions with same name");
*
* @return current view on transactions map.
*/
- public Map<String, ConfigTransactionControllerInternal> getCurrentTransactions() {
+ public Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> getCurrentTransactions() {
// first, remove closed transaction
- for (Iterator<Entry<String, ConfigTransactionControllerInternal>> it = transactions
+ for (Iterator<Entry<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>>> it = transactions
.entrySet().iterator(); it.hasNext(); ) {
- Entry<String, ConfigTransactionControllerInternal> entry = it
+ Entry<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> entry = it
.next();
- if (entry.getValue().isClosed()) {
+ if (entry.getValue().getKey().isClosed()) {
it.remove();
}
}
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
this.currentlyRegisteredFactories = currentlyRegisteredFactories;
this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(currentlyRegisteredFactories);
this.transactionStatus = new TransactionStatus();
- this.dependencyResolverManager = new DependencyResolverManager(transactionName, transactionStatus, writableSRRegistry, codecRegistry);
+ this.dependencyResolverManager = new DependencyResolverManager(txLookupRegistry.getTransactionIdentifier(),
+ transactionStatus, writableSRRegistry, codecRegistry);
this.transactionsMBeanServer = transactionsMBeanServer;
this.configMBeanServer = configMBeanServer;
this.blankTransaction = blankTransaction;
// put wrapper to jmx
TransactionModuleJMXRegistration transactionModuleJMXRegistration = getTxModuleJMXRegistrator()
.registerMBean(writableDynamicWrapper, writableON);
- ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
+
+ dependencyResolverManager.put(
moduleIdentifier, module, moduleFactory,
maybeOldConfigBeanInfo, transactionModuleJMXRegistration, isDefaultBean);
-
- dependencyResolverManager.put(moduleInternalTransactionalInfo);
return writableON;
}
logger.trace("Committed configuration {}", getTransactionIdentifier());
transactionStatus.setCommitted();
- // unregister this and all modules from jmx
- close();
return dependencyResolverManager.getSortedModuleIdentifiers();
}
}
public void close() {
- //FIXME: should not close object that was retrieved in constructor, a wrapper object should do that perhaps
- txLookupRegistry.close();
+ dependencyResolverManager.close();
}
@Override
return writableSRRegistry;
}
+ @Override
public TransactionIdentifier getTransactionIdentifier() {
return txLookupRegistry.getTransactionIdentifier();
}
* and {@link ConfigRegistryImpl} (consumer).
*/
interface ConfigTransactionControllerInternal extends
- ConfigTransactionControllerImplMXBean {
+ ConfigTransactionControllerImplMXBean, AutoCloseable {
ServiceReferenceWritableRegistry getWritableRegistry();
+ TransactionIdentifier getTransactionIdentifier();
+
+ @Override
+ void close();
}
--- /dev/null
+package org.opendaylight.controller.config.manager.impl;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+import java.util.concurrent.TimeUnit;
+
+public class DeadlockMonitor implements AutoCloseable {
+ private static final Logger logger = LoggerFactory.getLogger(DeadlockMonitorRunnable.class);
+
+ private static final long WARN_AFTER_MILLIS = 5000;
+
+ private final TransactionIdentifier transactionIdentifier;
+ private final DeadlockMonitorRunnable thread;
+ @GuardedBy("this")
+ private ModuleIdentifierWithNanos moduleIdentifierWithNanos = new ModuleIdentifierWithNanos();
+
+ public DeadlockMonitor(TransactionIdentifier transactionIdentifier) {
+ this.transactionIdentifier = transactionIdentifier;
+ thread = new DeadlockMonitorRunnable();
+ thread.start();
+ }
+
+ public synchronized void setCurrentlyInstantiatedModule(ModuleIdentifier currentlyInstantiatedModule) {
+ this.moduleIdentifierWithNanos = new ModuleIdentifierWithNanos(currentlyInstantiatedModule);
+ }
+
+ public boolean isAlive() {
+ return thread.isAlive();
+ }
+
+ @Override
+ public void close() {
+ thread.interrupt();
+ }
+
+ @Override
+ public String toString() {
+ return "DeadlockMonitor{" + transactionIdentifier + '}';
+ }
+
+ private class DeadlockMonitorRunnable extends Thread {
+
+ private DeadlockMonitorRunnable() {
+ super(DeadlockMonitor.this.toString());
+ }
+
+ @Override
+ public void run() {
+ ModuleIdentifierWithNanos old = new ModuleIdentifierWithNanos(); // null moduleId
+ while (this.isInterrupted() == false) {
+ ModuleIdentifierWithNanos copy = new ModuleIdentifierWithNanos(DeadlockMonitor.this.moduleIdentifierWithNanos);
+ if (old.moduleIdentifier == null) {
+ // started
+ old = copy;
+ } else if (old.moduleIdentifier != null && old.equals(copy)) {
+ // is the getInstance() running longer than WARN_AFTER_MILLIS ?
+ long runningTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - copy.nanoTime);
+ if (runningTime > WARN_AFTER_MILLIS) {
+ logger.warn("{} did not finish after {} ms", copy.moduleIdentifier, runningTime);
+ }
+ }
+ try {
+ sleep(1000);
+ } catch (InterruptedException e) {
+ interrupt();
+ }
+ }
+ logger.trace("Exiting {}", this);
+ }
+
+ @Override
+ public String toString() {
+ return "DeadLockMonitorRunnable{" + transactionIdentifier + "}";
+ }
+ }
+
+ private class ModuleIdentifierWithNanos {
+ @Nullable
+ private final ModuleIdentifier moduleIdentifier;
+ private final long nanoTime;
+
+ private ModuleIdentifierWithNanos() {
+ moduleIdentifier = null;
+ nanoTime = System.nanoTime();
+ }
+
+ private ModuleIdentifierWithNanos(ModuleIdentifier moduleIdentifier) {
+ this.moduleIdentifier = moduleIdentifier;
+ nanoTime = System.nanoTime();
+ }
+
+ private ModuleIdentifierWithNanos(ModuleIdentifierWithNanos copy) {
+ moduleIdentifier = copy.moduleIdentifier;
+ nanoTime = copy.nanoTime;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ModuleIdentifierWithNanos that = (ModuleIdentifierWithNanos) o;
+
+ if (nanoTime != that.nanoTime) return false;
+ if (moduleIdentifier != null ? !moduleIdentifier.equals(that.moduleIdentifier) : that.moduleIdentifier != null)
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = moduleIdentifier != null ? moduleIdentifier.hashCode() : 0;
+ result = 31 * result + (int) (nanoTime ^ (nanoTime >>> 32));
+ return result;
+ }
+ }
+}
import javax.annotation.Nullable;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.DestroyedModule;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
}
}
- // transalate from serviceref to module ON
+ // translate from serviceref to module ON
private ObjectName translateServiceRefIfPossible(ObjectName dependentReadOnlyON) {
if (ObjectNameUtil.isServiceReference(dependentReadOnlyON)) {
String serviceQName = ObjectNameUtil.getServiceQName(dependentReadOnlyON);
return maxDependencyDepth;
}
- public void countMaxDependencyDepth(DependencyResolverManager manager) {
+ void countMaxDependencyDepth(DependencyResolverManager manager) {
transactionStatus.checkCommitted();
if (maxDependencyDepth == null) {
maxDependencyDepth = getMaxDepth(this, manager,
public ModuleIdentifier getIdentifier() {
return name;
}
+
}
*/
package org.opendaylight.controller.config.manager.impl.dependencyresolver;
+import com.google.common.reflect.AbstractInvocationHandler;
+import com.google.common.reflect.Reflection;
import org.opendaylight.controller.config.api.DependencyResolver;
import org.opendaylight.controller.config.api.DependencyResolverFactory;
import org.opendaylight.controller.config.api.JmxAttribute;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
import org.opendaylight.controller.config.manager.impl.CommitInfo;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.DeadlockMonitor;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
import org.opendaylight.controller.config.manager.impl.TransactionStatus;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
import javax.annotation.concurrent.GuardedBy;
import javax.management.InstanceAlreadyExistsException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import static com.google.common.base.Preconditions.checkState;
+
/**
* Holds information about modules being created and destroyed within this
* transaction. Observes usage of DependencyResolver within modules to figure
* out dependency tree.
*/
-public class DependencyResolverManager implements TransactionHolder, DependencyResolverFactory {
+public class DependencyResolverManager implements DependencyResolverFactory, AutoCloseable {
@GuardedBy("this")
private final Map<ModuleIdentifier, DependencyResolverImpl> moduleIdentifiersToDependencyResolverMap = new HashMap<>();
private final ModulesHolder modulesHolder;
private final TransactionStatus transactionStatus;
private final ServiceReferenceReadableRegistry readableRegistry;
private final CodecRegistry codecRegistry;
+ private final DeadlockMonitor deadlockMonitor;
- public DependencyResolverManager(String transactionName,
+ public DependencyResolverManager(TransactionIdentifier transactionIdentifier,
TransactionStatus transactionStatus, ServiceReferenceReadableRegistry readableRegistry, CodecRegistry codecRegistry) {
- this.modulesHolder = new ModulesHolder(transactionName);
+ this.modulesHolder = new ModulesHolder(transactionIdentifier);
this.transactionStatus = transactionStatus;
this.readableRegistry = readableRegistry;
this.codecRegistry = codecRegistry;
+ this.deadlockMonitor = new DeadlockMonitor(transactionIdentifier);
}
@Override
return result;
}
- @Override
public ModuleInternalTransactionalInfo destroyModule(
ModuleIdentifier moduleIdentifier) {
transactionStatus.checkNotCommitted();
}
// protect write access
- @Override
+
public void put(
- ModuleInternalTransactionalInfo moduleInternalTransactionalInfo) {
+ final ModuleIdentifier moduleIdentifier,
+ final Module module,
+ ModuleFactory moduleFactory,
+ ModuleInternalInfo maybeOldInternalInfo,
+ TransactionModuleJMXRegistration transactionModuleJMXRegistration,
+ boolean isDefaultBean) {
transactionStatus.checkNotCommitted();
+
+ Class<? extends Module> moduleClass = Module.class;
+ if (module instanceof RuntimeBeanRegistratorAwareModule) {
+ moduleClass = RuntimeBeanRegistratorAwareModule.class;
+ }
+ Module proxiedModule = Reflection.newProxy(moduleClass, new AbstractInvocationHandler() {
+ // optimization: subsequent calls to getInstance MUST return the same value during transaction,
+ // so it is safe to cache the response
+ private Object cachedInstance;
+
+ @Override
+ protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
+ boolean isGetInstance = method.getName().equals("getInstance");
+ if (isGetInstance) {
+ if (cachedInstance != null) {
+ return cachedInstance;
+ }
+
+ checkState(deadlockMonitor.isAlive(), "Deadlock monitor is not alive");
+ deadlockMonitor.setCurrentlyInstantiatedModule(moduleIdentifier);
+ }
+ try {
+ Object response = method.invoke(module, args);
+ if (isGetInstance) {
+ cachedInstance = response;
+ }
+ return response;
+ } catch(InvocationTargetException e) {
+ throw e.getCause();
+ } finally {
+ if (isGetInstance) {
+ deadlockMonitor.setCurrentlyInstantiatedModule(null);
+ }
+ }
+ }
+ });
+
+
+ ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
+ moduleIdentifier, proxiedModule, moduleFactory,
+ maybeOldInternalInfo, transactionModuleJMXRegistration, isDefaultBean, module);
modulesHolder.put(moduleInternalTransactionalInfo);
}
// wrapped methods:
- @Override
public CommitInfo toCommitInfo() {
return modulesHolder.toCommitInfo();
}
- @Override
public Module findModule(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting) {
+ JmxAttribute jmxAttributeForReporting) {
return modulesHolder.findModule(moduleIdentifier,
jmxAttributeForReporting);
}
- @Override
public ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(ModuleIdentifier moduleIdentifier) {
return modulesHolder.findModuleInternalTransactionalInfo(moduleIdentifier);
}
- @Override
public ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting) {
+ JmxAttribute jmxAttributeForReporting) {
return modulesHolder.findModuleFactory(moduleIdentifier,
jmxAttributeForReporting);
}
- @Override
public Map<ModuleIdentifier, Module> getAllModules() {
return modulesHolder.getAllModules();
}
- @Override
public void assertNotExists(ModuleIdentifier moduleIdentifier)
throws InstanceAlreadyExistsException {
modulesHolder.assertNotExists(moduleIdentifier);
public List<ModuleIdentifier> findAllByFactory(ModuleFactory factory) {
List<ModuleIdentifier> result = new ArrayList<>();
- for( ModuleInternalTransactionalInfo info : modulesHolder.getAllInfos()) {
+ for (ModuleInternalTransactionalInfo info : modulesHolder.getAllInfos()) {
if (factory.equals(info.getModuleFactory())) {
result.add(info.getIdentifier());
}
}
return result;
}
+
+ public void close() {
+ deadlockMonitor.close();
+ }
+
}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.config.manager.impl;
+package org.opendaylight.controller.config.manager.impl.dependencyresolver;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
private final OsgiRegistration osgiRegistration;
private final int orderingIdx;
- DestroyedModule(ModuleIdentifier identifier, AutoCloseable instance,
+ public DestroyedModule(ModuleIdentifier identifier, AutoCloseable instance,
ModuleJMXRegistrator oldJMXRegistrator,
OsgiRegistration osgiRegistration, int orderingIdx) {
this.identifier = identifier;
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.config.manager.impl;
+package org.opendaylight.controller.config.manager.impl.dependencyresolver;
import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo;
import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
import org.opendaylight.controller.config.spi.Module;
public class ModuleInternalTransactionalInfo implements Identifiable<ModuleIdentifier> {
private final ModuleIdentifier name;
- private final Module module;
+ private final Module proxiedModule, realModule;
private final ModuleFactory moduleFactory;
@Nullable
private final ModuleInternalInfo maybeOldInternalInfo;
private final TransactionModuleJMXRegistration transactionModuleJMXRegistration;
private final boolean isDefaultBean;
- ModuleInternalTransactionalInfo(ModuleIdentifier name, Module module,
- ModuleFactory moduleFactory,
- ModuleInternalInfo maybeOldInternalInfo,
- TransactionModuleJMXRegistration transactionModuleJMXRegistration,
- boolean isDefaultBean) {
+ public ModuleInternalTransactionalInfo(ModuleIdentifier name, Module proxiedModule,
+ ModuleFactory moduleFactory,
+ ModuleInternalInfo maybeOldInternalInfo,
+ TransactionModuleJMXRegistration transactionModuleJMXRegistration,
+ boolean isDefaultBean, Module realModule) {
this.name = name;
- this.module = module;
+ this.proxiedModule = proxiedModule;
this.moduleFactory = moduleFactory;
this.maybeOldInternalInfo = maybeOldInternalInfo;
this.transactionModuleJMXRegistration = transactionModuleJMXRegistration;
this.isDefaultBean = isDefaultBean;
+ this.realModule = realModule;
}
}
- public Module getModule() {
- return module;
+ public Module getProxiedModule() {
+ return proxiedModule;
}
public ModuleFactory getModuleFactory() {
public boolean isDefaultBean() {
return isDefaultBean;
}
+
+ public Module getRealModule() {
+ return realModule;
+ }
}
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.manager.impl.CommitInfo;
-import org.opendaylight.controller.config.manager.impl.DestroyedModule;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.controller.config.spi.ModuleFactory;
/**
* Represents modules to be committed.
*/
-class ModulesHolder implements TransactionHolder {
- private final String transactionName;
+class ModulesHolder {
+ private final TransactionIdentifier transactionIdentifier;
@GuardedBy("this")
private final Map<ModuleIdentifier, ModuleInternalTransactionalInfo> commitMap = new HashMap<>();
@GuardedBy("this")
private final Set<ModuleInternalTransactionalInfo> unorderedDestroyedFromPreviousTransactions = new HashSet<>();
- ModulesHolder(String transactionName) {
- this.transactionName = transactionName;
+ ModulesHolder(TransactionIdentifier transactionIdentifier) {
+ this.transactionIdentifier = transactionIdentifier;
}
- @Override
+
public CommitInfo toCommitInfo() {
List<DestroyedModule> orderedDestroyedFromPreviousTransactions = new ArrayList<>(
unorderedDestroyedFromPreviousTransactions.size());
.get(moduleIdentifier);
JmxAttributeValidationException.checkNotNull(
moduleInternalTransactionalInfo, "Module " + moduleIdentifier
- + "" + " not found in transaction " + transactionName,
+ + "" + " not found in transaction " + transactionIdentifier,
jmxAttributeForReporting);
return moduleInternalTransactionalInfo;
}
- @Override
public Module findModule(ModuleIdentifier moduleIdentifier,
JmxAttribute jmxAttributeForReporting) {
return findModuleInternalTransactionalInfo(moduleIdentifier,
- jmxAttributeForReporting).getModule();
+ jmxAttributeForReporting).getProxiedModule();
}
- @Override
public ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
JmxAttribute jmxAttributeForReporting) {
return findModuleInternalTransactionalInfo(moduleIdentifier,
jmxAttributeForReporting).getModuleFactory();
}
- @Override
public Map<ModuleIdentifier, Module> getAllModules() {
Map<ModuleIdentifier, Module> result = new HashMap<>();
for (ModuleInternalTransactionalInfo entry : commitMap.values()) {
ModuleIdentifier name = entry.getIdentifier();
- result.put(name, entry.getModule());
+ result.put(name, entry.getProxiedModule());
}
return result;
}
- @Override
public void put(
ModuleInternalTransactionalInfo moduleInternalTransactionalInfo) {
commitMap.put(moduleInternalTransactionalInfo.getIdentifier(),
moduleInternalTransactionalInfo);
}
- @Override
public ModuleInternalTransactionalInfo destroyModule(
ModuleIdentifier moduleIdentifier) {
ModuleInternalTransactionalInfo found = commitMap.remove(moduleIdentifier);
return found;
}
- @Override
public void assertNotExists(ModuleIdentifier moduleIdentifier)
throws InstanceAlreadyExistsException {
if (commitMap.containsKey(moduleIdentifier)) {
return commitMap.values();
}
- @Override
public ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(ModuleIdentifier moduleIdentifier) {
ModuleInternalTransactionalInfo found = commitMap.get(moduleIdentifier);
if (found == null) {
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.manager.impl.dependencyresolver;
-
-import org.opendaylight.controller.config.api.JmxAttribute;
-import org.opendaylight.controller.config.api.ModuleIdentifier;
-import org.opendaylight.controller.config.manager.impl.CommitInfo;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
-import org.opendaylight.controller.config.spi.Module;
-import org.opendaylight.controller.config.spi.ModuleFactory;
-
-import javax.management.InstanceAlreadyExistsException;
-import java.util.Map;
-
-interface TransactionHolder {
- CommitInfo toCommitInfo();
-
- Module findModule(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting);
-
- ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
- JmxAttribute jmxAttributeForReporting);
-
- Map<ModuleIdentifier, Module> getAllModules();
-
- void put(ModuleInternalTransactionalInfo moduleInternalTransactionalInfo);
-
- ModuleInternalTransactionalInfo destroyModule(
- ModuleIdentifier moduleIdentifier);
-
- void assertNotExists(ModuleIdentifier moduleIdentifier)
- throws InstanceAlreadyExistsException;
-
- ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(ModuleIdentifier moduleIdentifier);
-
-}
import org.slf4j.LoggerFactory;
import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Collection;
import java.util.LinkedList;
errorMessage = logMessage("Class {} does not implement {} in bundle {}", clazz, YangModelBindingProvider.class, bundle);
throw new IllegalStateException(errorMessage);
}
-
+ YangModelBindingProvider instance;
try {
- Object instance = clazz.newInstance();
- Object result = clazz.getMethod(GET_MODULE_INFO_METHOD).invoke(instance);
-
- if (YangModuleInfo.class.isAssignableFrom(result.getClass()) == false) {
- errorMessage = logMessage("Error invoking method not found {} in bundle {}, reason {}",
- GET_MODULE_INFO_METHOD, bundle, "Not assignable from " + YangModuleInfo.class);
- } else {
- return (YangModuleInfo) result;
- }
-
+ Object instanceObj = clazz.newInstance();
+ instance = YangModelBindingProvider.class.cast(instanceObj);
} catch (InstantiationException e) {
errorMessage = logMessage("Could not instantiate {} in bundle {}, reason {}", moduleInfoClass, bundle, e);
+ throw new IllegalStateException(errorMessage, e);
} catch (IllegalAccessException e) {
errorMessage = logMessage("Illegal access during instatiation of class {} in bundle {}, reason {}",
moduleInfoClass, bundle, e);
- } catch (NoSuchMethodException e) {
- errorMessage = logMessage("Method not found {} in bundle {}, reason {}", GET_MODULE_INFO_METHOD, bundle, e);
- } catch (InvocationTargetException e) {
- errorMessage = logMessage("Error invoking method {} in bundle {}, reason {}", GET_MODULE_INFO_METHOD,
- bundle, e);
+ throw new IllegalStateException(errorMessage, e);
}
+ try{
+ return instance.getModuleInfo();
+ } catch (NoClassDefFoundError e) {
- throw new IllegalStateException(errorMessage);
+
+ logger.error("Error while executing getModuleInfo on {}", instance, e);
+ throw e;
+ }
}
private static Class<?> loadClass(String moduleInfoClass, Bundle bundle) {
*/
package org.opendaylight.controller.config.manager.impl.dependencyresolver;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-
-import java.util.Arrays;
-import java.util.List;
-
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.config.api.JmxAttribute;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
-import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
import org.opendaylight.controller.config.manager.impl.TransactionStatus;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
public class DependencyResolverManagerTest {
public void setUp() {
transactionStatus = mock(TransactionStatus.class);
ServiceReferenceReadableRegistry mockedRegistry = mock(ServiceReferenceReadableRegistry.class);
- tested = new DependencyResolverManager("txName", transactionStatus, mockedRegistry, null);
+ tested = new DependencyResolverManager(new TransactionIdentifier("txName"), transactionStatus, mockedRegistry, null);
doNothing().when(transactionStatus).checkCommitStarted();
doNothing().when(transactionStatus).checkNotCommitted();
}
private static void mockGetInstance(DependencyResolverManager tested,
ModuleIdentifier moduleIdentifier) {
- ModuleInternalTransactionalInfo mock = mock(ModuleInternalTransactionalInfo.class);
- doReturn(moduleIdentifier).when(mock).getIdentifier();
- doReturn(mockedModule()).when(mock).getModule();
- tested.put(mock);
+
+ ModuleFactory moduleFactory = mock(ModuleFactory.class);
+ ModuleInternalInfo maybeOldInternalInfo = null;
+ TransactionModuleJMXRegistration transactionModuleJMXRegistration = null;
+ boolean isDefaultBean = false;
+
+ tested.put(moduleIdentifier,
+ mockedModule(),
+ moduleFactory,
+ maybeOldInternalInfo,
+ transactionModuleJMXRegistration,
+ isDefaultBean);
}
private static Module mockedModule() {
*/
package org.opendaylight.controller.config.manager.testingservices.parallelapsp;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.Closeable;
-
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.management.ObjectName;
-
+import com.google.common.base.Strings;
import org.opendaylight.controller.config.api.DependencyResolver;
import org.opendaylight.controller.config.api.JmxAttribute;
import org.opendaylight.controller.config.api.ModuleIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Strings;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.management.ObjectName;
+import java.io.Closeable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
/**
* Represents service that has dependency to thread pool.
}
@Test
- public void testAbort() {
+ public void testAbort() throws InstanceAlreadyExistsException, ValidationException {
ConfigTransactionJMXClient transaction = configRegistryClient
.createTransaction();
assertEquals(1, configRegistryClient.getOpenConfigs().size());
transaction.createModule(TestingFixedThreadPoolModuleFactory.NAME,
fixed1);
fail();
- } catch (Exception e) {
- assertTrue(e.getCause() instanceof InstanceNotFoundException);
+ } catch (IllegalStateException e) {
+ assertEquals("Configuration was aborted", e.getMessage());
}
try {
transaction.validateConfig();
fail();
- } catch (Exception e) {
- assertTrue(e.getCause() instanceof InstanceNotFoundException);
+ } catch (IllegalStateException e) {
+ assertEquals("Configuration was aborted", e.getMessage());
}
assertEquals(0, configRegistryClient.getOpenConfigs().size());
}
private static final Logger logger = LoggerFactory.getLogger(Activator.class);
private BundleContext context;
- ServiceRegistration osgiRegistration;
+ private ServiceRegistration osgiRegistration;
+ private ConfigRegistryLookupThread configRegistryLookup = null;
@Override
public void start(BundleContext context) throws Exception {
@Override
public void stop(BundleContext context) throws Exception {
+ if (configRegistryLookup != null) {
+ configRegistryLookup.interrupt();
+ }
}
@Override
public synchronized void onYangStoreAdded(YangStoreService yangStoreService) {
- checkState(osgiRegistration == null, "More than one onYangStoreAdded received");
- NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
- logger.debug("Registering into OSGi");
- osgiRegistration = context.registerService(new String[]{NetconfOperationServiceFactory.class.getName()}, factory,
- new Hashtable<String, Object>());
+ checkState(configRegistryLookup == null, "More than one onYangStoreAdded received");
+ configRegistryLookup = new ConfigRegistryLookupThread(yangStoreService);
+ configRegistryLookup.start();
}
@Override
public synchronized void onYangStoreRemoved() {
- osgiRegistration.unregister();
+ configRegistryLookup.interrupt();
+ if (osgiRegistration != null) {
+ osgiRegistration.unregister();
+ }
osgiRegistration = null;
+ configRegistryLookup = null;
+ }
+
+ private class ConfigRegistryLookupThread extends Thread {
+ private final YangStoreService yangStoreService;
+
+ private ConfigRegistryLookupThread(YangStoreService yangStoreService) {
+ super("config-registry-lookup");
+ this.yangStoreService = yangStoreService;
+ }
+
+ @Override
+ public void run() {
+ NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService);
+ logger.debug("Registering into OSGi");
+ osgiRegistration = context.registerService(new String[]{NetconfOperationServiceFactory.class.getName()}, factory,
+ new Hashtable<String, Object>());
+ }
}
}