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 {@code 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 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();
//deprecated
private final AtomicReference schedulerOverrides = new AtomicReference();
private final AtomicReference schedulersHook = new AtomicReference();
/**
* Retrieves the single {@code RxJavaPlugins} instance.
*
* @return the single {@code RxJavaPlugins} instance
*/
public static RxJavaPlugins getInstance() {
return INSTANCE;
}
/* package accessible for unit tests */RxJavaPlugins() {
}
/* package accessible for unit tests */void reset() {
INSTANCE.errorHandler.set(null);
INSTANCE.observableExecutionHook.set(null);
INSTANCE.schedulersHook.set(null);
}
/**
* Retrieves an instance of {@link RxJavaErrorHandler} to use based on order of precedence as defined in
* {@link RxJavaPlugins} class header.
*
* Override the default by calling {@link #registerErrorHandler(RxJavaErrorHandler)} or by setting the
* property {@code 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();
}
/**
* Registers an {@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());
}
}
/**
* Retrieves the instance of {@link RxJavaObservableExecutionHook} to use based on order of precedence as
* defined in {@link RxJavaPlugins} class header.
*
* Override the default by calling {@link #registerObservableExecutionHook(RxJavaObservableExecutionHook)}
* or by setting the property {@code 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 an {@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;
}
}
/**
* Retrieves the instance of {@link RxJavaDefaultSchedulers} to use based on order of precedence as defined
* in the {@link RxJavaPlugins} class header.
*
* Override the default by calling {@link #registerDefaultSchedulers(RxJavaDefaultSchedulers)} or by setting
* the property {@code rxjava.plugin.RxJavaDefaultSchedulers.implementation} with the full classname to
* load.
*
* @return the {@link RxJavaDefaultSchedulers} implementation in 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();
}
/**
* Registers an {@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());
}
}
/**
* Retrieves the instance of {@link RxJavaSchedulersHook} to use based on order of precedence as defined
* in the {@link RxJavaPlugins} class header.
*
* Override the default by calling {@link #registerSchedulersHook(RxJavaSchedulersHook)} or by setting
* the property {@code rxjava.plugin.RxJavaSchedulersHook.implementation} with the full classname to
* load.
*
* @return the {@link RxJavaSchedulersHook} implementation in use
*/
public RxJavaSchedulersHook getSchedulersHook() {
if (schedulersHook.get() == null) {
// check for an implementation from System.getProperty first
Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class);
if (impl == null) {
// nothing set via properties so initialize with default
schedulersHook.compareAndSet(null, RxJavaSchedulersHook.getDefaultInstance());
// 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
schedulersHook.compareAndSet(null, (RxJavaSchedulersHook) impl);
}
}
return schedulersHook.get();
}
/**
* Registers an {@link RxJavaSchedulersHook} implementation as a global override of any injected or
* default implementations.
*
* @param impl
* {@link RxJavaSchedulersHook} implementation
* @throws IllegalStateException
* if called more than once or after the default was initialized (if usage occurs before trying
* to register)
*/
public void registerSchedulersHook(RxJavaSchedulersHook impl) {
if (!schedulersHook.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered: " + schedulersHook.get());
}
}
}