From c7e2ecf90c4850ab8f7c6bd33eab8bb1e6db01e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Karnok?= Date: Wed, 11 Nov 2015 19:47:25 +0100 Subject: [PATCH] 1.x: add shorter RxJavaPlugin class lookup approach. --- src/main/java/rx/plugins/RxJavaPlugins.java | 66 ++++++++++++++++--- .../java/rx/plugins/RxJavaPluginsTest.java | 46 +++++++++---- 2 files changed, 89 insertions(+), 23 deletions(-) diff --git a/src/main/java/rx/plugins/RxJavaPlugins.java b/src/main/java/rx/plugins/RxJavaPlugins.java index 2e48305989..09e542779d 100644 --- a/src/main/java/rx/plugins/RxJavaPlugins.java +++ b/src/main/java/rx/plugins/RxJavaPlugins.java @@ -15,6 +15,7 @@ */ package rx.plugins; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; /** @@ -26,7 +27,22 @@ * property names) *
  • default implementation
  • * - * + *

    In addition to the {@code rxjava.plugin.[simple classname].implementation} system properties, + * you can define two system property:
    + *

    
    + * rxjava.plugin.[index].class}
    + * rxjava.plugin.[index].impl}
    + * 
    + * + * Where the {@code .class} property contains the simple classname from above and the {@code .impl} + * contains the fully qualified name of the implementation class. The {@code [index]} can be + * any short string or number of your chosing. For example, you can now define a custom + * {@code RxJavaErrorHandler} via two system property: + *
    
    + * rxjava.plugin.1.class=RxJavaErrorHandler
    + * rxjava.plugin.1.impl=some.package.MyRxJavaErrorHandler
    + * 
    + * * @see RxJava Wiki: Plugins */ public class RxJavaPlugins { @@ -64,13 +80,12 @@ public static RxJavaPlugins getInstance() { *

    * 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); + Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class, System.getProperties()); if (impl == null) { // nothing set via properties so initialize with default errorHandler.compareAndSet(null, DEFAULT_ERROR_HANDLER); @@ -112,7 +127,7 @@ public void registerErrorHandler(RxJavaErrorHandler impl) { public RxJavaObservableExecutionHook getObservableExecutionHook() { if (observableExecutionHook.get() == null) { // check for an implementation from System.getProperty first - Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class); + Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class, System.getProperties()); if (impl == null) { // nothing set via properties so initialize with default observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance()); @@ -141,15 +156,46 @@ public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl) } } - private static Object getPluginImplementationViaProperty(Class pluginClass) { - String classSimpleName = pluginClass.getSimpleName(); + /* test */ static Object getPluginImplementationViaProperty(Class pluginClass, Properties props) { + final 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"); + + final String pluginPrefix = "rxjava.plugin."; + + String defaultKey = pluginPrefix + classSimpleName + ".implementation"; + String implementingClass = props.getProperty(defaultKey); + + if (implementingClass == null) { + final String classSuffix = ".class"; + final String implSuffix = ".impl"; + + for (Map.Entry e : props.entrySet()) { + String key = e.getKey().toString(); + if (key.startsWith(pluginPrefix) && key.endsWith(classSuffix)) { + String value = e.getValue().toString(); + + if (classSimpleName.equals(value)) { + String index = key.substring(0, key.length() - classSuffix.length()).substring(pluginPrefix.length()); + + String implKey = pluginPrefix + index + implSuffix; + + implementingClass = props.getProperty(implKey); + + if (implementingClass == null) { + throw new RuntimeException("Implementing class declaration for " + classSimpleName + " missing: " + implKey); + } + + break; + } + } + } + } + if (implementingClass != null) { try { Class cls = Class.forName(implementingClass); @@ -165,9 +211,9 @@ private static Object getPluginImplementationViaProperty(Class pluginClass) { } catch (IllegalAccessException e) { throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e); } - } else { - return null; } + + return null; } /** @@ -183,7 +229,7 @@ private static Object getPluginImplementationViaProperty(Class pluginClass) { public RxJavaSchedulersHook getSchedulersHook() { if (schedulersHook.get() == null) { // check for an implementation from System.getProperty first - Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class); + Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class, System.getProperties()); if (impl == null) { // nothing set via properties so initialize with default schedulersHook.compareAndSet(null, RxJavaSchedulersHook.getDefaultInstance()); diff --git a/src/test/java/rx/plugins/RxJavaPluginsTest.java b/src/test/java/rx/plugins/RxJavaPluginsTest.java index 3d18923915..e4cd9f69ae 100644 --- a/src/test/java/rx/plugins/RxJavaPluginsTest.java +++ b/src/test/java/rx/plugins/RxJavaPluginsTest.java @@ -15,21 +15,12 @@ */ package rx.plugins; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; +import static org.junit.Assert.*; + +import java.util.*; import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import rx.Observable; import rx.Subscriber; @@ -251,4 +242,33 @@ private static String getFullClassNameForTestClass(Class cls) { return RxJavaPlugins.class.getPackage() .getName() + "." + RxJavaPluginsTest.class.getSimpleName() + "$" + cls.getSimpleName(); } + + @Test + public void testShortPluginDiscovery() { + Properties props = new Properties(); + + props.setProperty("rxjava.plugin.1.class", "Map"); + props.setProperty("rxjava.plugin.1.impl", "java.util.HashMap"); + + props.setProperty("rxjava.plugin.xyz.class", "List"); + props.setProperty("rxjava.plugin.xyz.impl", "java.util.ArrayList"); + + + Object o = RxJavaPlugins.getPluginImplementationViaProperty(Map.class, props); + + assertTrue("" + o, o instanceof HashMap); + + o = RxJavaPlugins.getPluginImplementationViaProperty(List.class, props); + + assertTrue("" + o, o instanceof ArrayList); + } + + @Test(expected = RuntimeException.class) + public void testShortPluginDiscoveryMissing() { + Properties props = new Properties(); + + props.setProperty("rxjava.plugin.1.class", "Map"); + + RxJavaPlugins.getPluginImplementationViaProperty(Map.class, props); + } }