rx.plugins.RxJavaPlugins Maven / Gradle / Ivy
Show all versions of rxjava-core Show documentation
/**
* Copyright 2014 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.plugins;
import java.util.concurrent.atomic.AtomicReference;
/**
* Registry for plugin implementations that allows global override and handles the retrieval of correct implementation based on order of precedence:
*
* - plugin registered globally via
register
methods in this class
* - plugin registered and retrieved using {@link java.lang.System#getProperty(String)} (see get methods for property names)
* - default implementation
*
* See the RxJava GitHub Wiki for more information: https://github.com/Netflix/RxJava/wiki/Plugins.
*/
public class RxJavaPlugins {
private final static RxJavaPlugins INSTANCE = new RxJavaPlugins();
private final AtomicReference errorHandler = new AtomicReference();
private final AtomicReference observableExecutionHook = new AtomicReference();
private final AtomicReference schedulerOverrides = new AtomicReference();
public static RxJavaPlugins getInstance() {
return INSTANCE;
}
/* package accessible for unit tests */RxJavaPlugins() {
}
/* package accessible for ujnit tests */void reset() {
INSTANCE.errorHandler.set(null);
INSTANCE.observableExecutionHook.set(null);
}
/**
* Retrieve instance of {@link RxJavaErrorHandler} to use based on order of precedence as defined in {@link RxJavaPlugins} class header.
*
* Override default by using {@link #registerErrorHandler(RxJavaErrorHandler)} or setting property: rxjava.plugin.RxJavaErrorHandler.implementation
with the full classname to
* load.
*
* @return {@link RxJavaErrorHandler} implementation to use
*/
public RxJavaErrorHandler getErrorHandler() {
if (errorHandler.get() == null) {
// check for an implementation from System.getProperty first
Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class);
if (impl == null) {
// nothing set via properties so initialize with default
errorHandler.compareAndSet(null, RxJavaErrorHandlerDefault.getInstance());
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
} else {
// we received an implementation from the system property so use it
errorHandler.compareAndSet(null, (RxJavaErrorHandler) impl);
}
}
return errorHandler.get();
}
/**
* Register a {@link RxJavaErrorHandler} implementation as a global override of any injected or default implementations.
*
* @param impl
* {@link RxJavaErrorHandler} implementation
* @throws IllegalStateException
* if called more than once or after the default was initialized (if usage occurs before trying to register)
*/
public void registerErrorHandler(RxJavaErrorHandler impl) {
if (!errorHandler.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered: " + errorHandler.get());
}
}
/**
* Retrieve instance of {@link RxJavaObservableExecutionHook} to use based on order of precedence as defined in {@link RxJavaPlugins} class header.
*
* Override default by using {@link #registerObservableExecutionHook(RxJavaObservableExecutionHook)} or setting property: rxjava.plugin.RxJavaObservableExecutionHook.implementation
* with the full classname to load.
*
* @return {@link RxJavaObservableExecutionHook} implementation to use
*/
public RxJavaObservableExecutionHook getObservableExecutionHook() {
if (observableExecutionHook.get() == null) {
// check for an implementation from System.getProperty first
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class);
if (impl == null) {
// nothing set via properties so initialize with default
observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance());
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
} else {
// we received an implementation from the system property so use it
observableExecutionHook.compareAndSet(null, (RxJavaObservableExecutionHook) impl);
}
}
return observableExecutionHook.get();
}
/**
* Register a {@link RxJavaObservableExecutionHook} implementation as a global override of any injected or default implementations.
*
* @param impl
* {@link RxJavaObservableExecutionHook} implementation
* @throws IllegalStateException
* if called more than once or after the default was initialized (if usage occurs before trying to register)
*/
public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl) {
if (!observableExecutionHook.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered: " + observableExecutionHook.get());
}
}
private static Object getPluginImplementationViaProperty(Class> pluginClass) {
String classSimpleName = pluginClass.getSimpleName();
/*
* Check system properties for plugin class.
*
* This will only happen during system startup thus it's okay to use the synchronized System.getProperties
* as it will never get called in normal operations.
*/
String implementingClass = System.getProperty("rxjava.plugin." + classSimpleName + ".implementation");
if (implementingClass != null) {
try {
Class> cls = Class.forName(implementingClass);
// narrow the scope (cast) to the type we're expecting
cls = cls.asSubclass(pluginClass);
return cls.newInstance();
} catch (ClassCastException e) {
throw new RuntimeException(classSimpleName + " implementation is not an instance of " + classSimpleName + ": " + implementingClass);
} catch (ClassNotFoundException e) {
throw new RuntimeException(classSimpleName + " implementation class not found: " + implementingClass, e);
} catch (InstantiationException e) {
throw new RuntimeException(classSimpleName + " implementation not able to be instantiated: " + implementingClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e);
}
} else {
return null;
}
}
/**
* Retrieve instance of {@link RxJavaDefaultSchedulers} to use based on order of precedence as defined in {@link RxJavaPlugins} class header.
*
* Override default by using {@link #registerDefaultSchedulers(RxJavaDefaultSchedulers)} or setting property: rxjava.plugin.RxJavaDefaultSchedulers.implementation
with the full
* classname to
* load.
*
* @return {@link RxJavaErrorHandler} implementation to use
*/
public RxJavaDefaultSchedulers getDefaultSchedulers() {
if (schedulerOverrides.get() == null) {
// check for an implementation from System.getProperty first
Object impl = getPluginImplementationViaProperty(RxJavaDefaultSchedulers.class);
if (impl == null) {
// nothing set via properties so initialize with default
schedulerOverrides.compareAndSet(null, RxJavaDefaultSchedulersDefault.getInstance());
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
} else {
// we received an implementation from the system property so use it
schedulerOverrides.compareAndSet(null, (RxJavaDefaultSchedulers) impl);
}
}
return schedulerOverrides.get();
}
/**
* Register a {@link RxJavaDefaultSchedulers} implementation as a global override of any injected or default implementations.
*
* @param impl
* {@link RxJavaDefaultSchedulers} implementation
* @throws IllegalStateException
* if called more than once or after the default was initialized (if usage occurs before trying to register)
*/
public void registerDefaultSchedulers(RxJavaDefaultSchedulers impl) {
if (!schedulerOverrides.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered: " + schedulerOverrides.get());
}
}
}