/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.common;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.communication.api.NodeIdentifierService;
/**
* A static holder used to provide a required service instance to (potentially hidden/transitive) deserialiation calls of
* {@link CommonIdBase} subclasses.
*
* @author Robert Mischke
*/
public final class NodeIdentifierContextHolder {
private static final String SERVICE_CLASS_NAME = NodeIdentifierService.class.getSimpleName();
private static final transient ThreadLocal<NodeIdentifierService> sharedServiceThreadLocal = new ThreadLocal<>();
private static volatile NodeIdentifierService defaultNodeIdentifierService;
private static final Log sharedLog = LogFactory.getLog(NodeIdentifierContextHolder.class);
public NodeIdentifierContextHolder() {}
/**
* Sets the service instance that should be used for deserializing id objects within the current {@link Thread}. Using this approach
* instead of a singleton is important for proper unit/integration testing.
*
* @param serviceInstance the service instance to set
* @return the former service instance, if any; useful for restoring a previous context after an operation
*/
public static NodeIdentifierService setDeserializationServiceForCurrentThread(NodeIdentifierService serviceInstance) {
NodeIdentifierService previous = sharedServiceThreadLocal.get();
sharedServiceThreadLocal.set(serviceInstance);
return previous;
}
/**
* Retrieves the service instance that should be used for deserializing id objects within the current {@link Thread}. Using this
* approach instead of a singleton is important for proper unit/integration testing.
*
* @return the service instance set for the current {@link Thread}
*/
public static NodeIdentifierService getRawDeserializationServiceForCurrentThread() {
return sharedServiceThreadLocal.get();
}
/**
* Retrieves the service instance that should be used for deserializing id objects within the current {@link Thread}. Using this
* approach instead of a singleton is important for proper unit/integration testing.
*
* @return the service instance set for the current {@link Thread}
*/
public static NodeIdentifierService getDeserializationServiceForCurrentThread() {
final NodeIdentifierService result = sharedServiceThreadLocal.get();
if (result == null) {
if (defaultNodeIdentifierService != null) {
// TODO >8.0 switch to ThreadContext once it is available in all threading contexts
// sharedLog.debug("Using the global default " + SERVICE_CLASS_NAME + " instance; consider setting this explicitly through "
// + NodeIdentifierContextHolder.class.getSimpleName());
return defaultNodeIdentifierService;
}
// TODO stopgap solution until a more generic approach is implemented
NodeIdentifierService testNodeIdentifierService = NodeIdentifierTestUtils.getTestNodeIdentifierService();
if (testNodeIdentifierService != null) {
sharedLog.warn("There is no " + SERVICE_CLASS_NAME
+ " instance registered for the current thread; falling back to the default test instance");
return testNodeIdentifierService;
}
throw new IllegalStateException(
"There is no " + SERVICE_CLASS_NAME
+ " instance registered for the current thread, which is required to deserialize distributed node identifiers, "
+ "and there is no global default instance (typical in test contexts); use "
+ NodeIdentifierContextHolder.class.getName() + " methods to fix this");
}
return result;
}
/**
* OSGi-DS bind method; the "live" service instance is created and injected here by OSGi-DS. This service instance is used as a fallback
* when the current thread has no explicit service set. This prevents live code from failing in rarely-used code parts.
*
* In integration testing, no such default instance is available, which causes an exception to be thrown. This is intentional to locate
* these places and add explicit thread context setting there.
*
* @param newInstance the new service instance
*/
public static synchronized void bindNodeIdentifierService(NodeIdentifierService newInstance) {
if (defaultNodeIdentifierService != null) {
// ensure only one static service is ever called for consistency
throw new IllegalStateException("Tried to set a NodeIdentifierService instance, but it was already defined");
}
defaultNodeIdentifierService = newInstance;
LogFactory.getLog(NodeIdentifierContextHolder.class).debug("Injected live " + newInstance.getClass() + " instance");
}
/**
* Transitional method to prevent CheckStyle from complaining that this method should have a private constructor, which must however be
* public to allow static OSGi-DS injection (sigh...). This will disappear once no more code uses this factory outside unit/integration
* tests.
*/
public void transitionalDummy() {}
}