Skip to content

Commit 69b32a8

Browse files
committed
1.x: add shorter RxJavaPlugin class lockup approach.
This adds a new pattern and lookup method that let's the developer specify the custom plugins with shorter system property keys. Android is quite restrictive and allows only 31 characters. The new pattern splits the target simple class name and its implementation into two separate system properties: ``` rxjava.plugin.1.class=SimpleClassName rxjava.plugin.1.impl=path.to.impl.Class ``` The index tag (`1`) can be any string of your chosing: ``` rxjava.plugin.mykey.class=SimpleClassName rxjava.plugin.mykey.impl=path.to.impl.Class ``` but make sure they are paired, otherwise nothing will happen. If there are multiple `class` entries with the same `SimpleClassName` one of them will be chosen (depending on the walk order in `java.util.Properties`).
1 parent 3e2b3b1 commit 69b32a8

File tree

2 files changed

+68
-22
lines changed

2 files changed

+68
-22
lines changed

src/main/java/rx/plugins/RxJavaPlugins.java

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package rx.plugins;
1717

18+
import java.util.*;
1819
import java.util.concurrent.atomic.AtomicReference;
1920

2021
/**
@@ -26,7 +27,22 @@
2627
* property names)</li>
2728
* <li>default implementation</li>
2829
* </ol>
29-
*
30+
* <p>In addition to the {@code rxjava.plugin.[simple classname].implementation} system properties,
31+
* you can define two system property:<br>
32+
* <pre><code>
33+
* rxjava.plugin.[index].class}
34+
* rxjava.plugin.[index].impl}
35+
* </code></pre>
36+
*
37+
* Where the {@code .class} property contains the simple classname from above and the {@code .impl}
38+
* contains the fully qualified name of the implementation class. The {@code [index]} can be
39+
* any short string or number of your chosing. For example, you can now define a custom
40+
* {@code RxJavaErrorHandler} via two system property:
41+
* <pre><code>
42+
* rxjava.plugin.1.class=RxJavaErrorHandler
43+
* rxjava.plugin.1.impl=some.package.MyRxJavaErrorHandler
44+
* </code></pre>
45+
*
3046
* @see <a href="https://github.com/ReactiveX/RxJava/wiki/Plugins">RxJava Wiki: Plugins</a>
3147
*/
3248
public class RxJavaPlugins {
@@ -64,13 +80,12 @@ public static RxJavaPlugins getInstance() {
6480
* <p>
6581
* Override the default by calling {@link #registerErrorHandler(RxJavaErrorHandler)} or by setting the
6682
* property {@code rxjava.plugin.RxJavaErrorHandler.implementation} with the full classname to load.
67-
*
6883
* @return {@link RxJavaErrorHandler} implementation to use
6984
*/
7085
public RxJavaErrorHandler getErrorHandler() {
7186
if (errorHandler.get() == null) {
7287
// check for an implementation from System.getProperty first
73-
Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class);
88+
Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class, System.getProperties());
7489
if (impl == null) {
7590
// nothing set via properties so initialize with default
7691
errorHandler.compareAndSet(null, DEFAULT_ERROR_HANDLER);
@@ -112,7 +127,7 @@ public void registerErrorHandler(RxJavaErrorHandler impl) {
112127
public RxJavaObservableExecutionHook getObservableExecutionHook() {
113128
if (observableExecutionHook.get() == null) {
114129
// check for an implementation from System.getProperty first
115-
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class);
130+
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class, System.getProperties());
116131
if (impl == null) {
117132
// nothing set via properties so initialize with default
118133
observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance());
@@ -141,15 +156,35 @@ public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl)
141156
}
142157
}
143158

144-
private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
159+
/* test */ static Object getPluginImplementationViaProperty(Class<?> pluginClass, Properties props) {
145160
String classSimpleName = pluginClass.getSimpleName();
146161
/*
147162
* Check system properties for plugin class.
148163
* <p>
149164
* This will only happen during system startup thus it's okay to use the synchronized
150165
* System.getProperties as it will never get called in normal operations.
151166
*/
152-
String implementingClass = System.getProperty("rxjava.plugin." + classSimpleName + ".implementation");
167+
168+
String defaultKey = "rxjava.plugin." + classSimpleName + ".implementation";
169+
String implementingClass = props.getProperty(defaultKey);
170+
171+
for (Map.Entry<Object, Object> e : props.entrySet()) {
172+
String key = e.getKey().toString();
173+
if (key.startsWith("rxjava.plugin.") && key.endsWith(".class")) {
174+
String value = e.getValue().toString();
175+
176+
if (classSimpleName.equals(value)) {
177+
String index = key.substring(0, key.length() - 6).substring(14);
178+
179+
String implKey = "rxjava.plugin." + index + ".impl";
180+
181+
implementingClass = props.getProperty(implKey);
182+
183+
break;
184+
}
185+
}
186+
}
187+
153188
if (implementingClass != null) {
154189
try {
155190
Class<?> cls = Class.forName(implementingClass);
@@ -165,9 +200,9 @@ private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
165200
} catch (IllegalAccessException e) {
166201
throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e);
167202
}
168-
} else {
169-
return null;
170203
}
204+
205+
return null;
171206
}
172207

173208
/**
@@ -183,7 +218,7 @@ private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
183218
public RxJavaSchedulersHook getSchedulersHook() {
184219
if (schedulersHook.get() == null) {
185220
// check for an implementation from System.getProperty first
186-
Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class);
221+
Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class, System.getProperties());
187222
if (impl == null) {
188223
// nothing set via properties so initialize with default
189224
schedulersHook.compareAndSet(null, RxJavaSchedulersHook.getDefaultInstance());

src/test/java/rx/plugins/RxJavaPluginsTest.java

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,12 @@
1515
*/
1616
package rx.plugins;
1717

18-
import static org.junit.Assert.assertEquals;
19-
import static org.junit.Assert.assertNotNull;
20-
import static org.junit.Assert.assertNull;
21-
import static org.junit.Assert.assertSame;
22-
import static org.junit.Assert.assertTrue;
23-
import static org.junit.Assert.fail;
24-
25-
import java.util.Calendar;
26-
import java.util.Collections;
27-
import java.util.Date;
18+
import static org.junit.Assert.*;
19+
20+
import java.util.*;
2821
import java.util.concurrent.TimeUnit;
2922

30-
import org.junit.After;
31-
import org.junit.Before;
32-
import org.junit.Test;
23+
import org.junit.*;
3324

3425
import rx.Observable;
3526
import rx.Subscriber;
@@ -251,4 +242,24 @@ private static String getFullClassNameForTestClass(Class<?> cls) {
251242
return RxJavaPlugins.class.getPackage()
252243
.getName() + "." + RxJavaPluginsTest.class.getSimpleName() + "$" + cls.getSimpleName();
253244
}
245+
246+
@Test
247+
public void testShortPluginDiscovery() {
248+
Properties props = new Properties();
249+
250+
props.setProperty("rxjava.plugin.1.class", "Map");
251+
props.setProperty("rxjava.plugin.1.impl", "java.util.HashMap");
252+
253+
props.setProperty("rxjava.plugin.xyz.class", "List");
254+
props.setProperty("rxjava.plugin.xyz.impl", "java.util.ArrayList");
255+
256+
257+
Object o = RxJavaPlugins.getPluginImplementationViaProperty(Map.class, props);
258+
259+
assertTrue("" + o, o instanceof HashMap);
260+
261+
o = RxJavaPlugins.getPluginImplementationViaProperty(List.class, props);
262+
263+
assertTrue("" + o, o instanceof ArrayList);
264+
}
254265
}

0 commit comments

Comments
 (0)