15
15
*/
16
16
package rx .plugins ;
17
17
18
+ import java .util .*;
18
19
import java .util .concurrent .atomic .AtomicReference ;
19
20
20
21
/**
26
27
* property names)</li>
27
28
* <li>default implementation</li>
28
29
* </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
+ *
30
46
* @see <a href="https://github.com/ReactiveX/RxJava/wiki/Plugins">RxJava Uncyclo: Plugins</a>
31
47
*/
32
48
public class RxJavaPlugins {
@@ -64,13 +80,12 @@ public static RxJavaPlugins getInstance() {
64
80
* <p>
65
81
* Override the default by calling {@link #registerErrorHandler(RxJavaErrorHandler)} or by setting the
66
82
* property {@code rxjava.plugin.RxJavaErrorHandler.implementation} with the full classname to load.
67
- *
68
83
* @return {@link RxJavaErrorHandler} implementation to use
69
84
*/
70
85
public RxJavaErrorHandler getErrorHandler () {
71
86
if (errorHandler .get () == null ) {
72
87
// check for an implementation from System.getProperty first
73
- Object impl = getPluginImplementationViaProperty (RxJavaErrorHandler .class );
88
+ Object impl = getPluginImplementationViaProperty (RxJavaErrorHandler .class , System . getProperties () );
74
89
if (impl == null ) {
75
90
// nothing set via properties so initialize with default
76
91
errorHandler .compareAndSet (null , DEFAULT_ERROR_HANDLER );
@@ -112,7 +127,7 @@ public void registerErrorHandler(RxJavaErrorHandler impl) {
112
127
public RxJavaObservableExecutionHook getObservableExecutionHook () {
113
128
if (observableExecutionHook .get () == null ) {
114
129
// check for an implementation from System.getProperty first
115
- Object impl = getPluginImplementationViaProperty (RxJavaObservableExecutionHook .class );
130
+ Object impl = getPluginImplementationViaProperty (RxJavaObservableExecutionHook .class , System . getProperties () );
116
131
if (impl == null ) {
117
132
// nothing set via properties so initialize with default
118
133
observableExecutionHook .compareAndSet (null , RxJavaObservableExecutionHookDefault .getInstance ());
@@ -141,15 +156,42 @@ public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl)
141
156
}
142
157
}
143
158
144
- private static Object getPluginImplementationViaProperty (Class <?> pluginClass ) {
145
- String classSimpleName = pluginClass .getSimpleName ();
159
+ /* test */ static Object getPluginImplementationViaProperty (Class <?> pluginClass , Properties props ) {
160
+ final String classSimpleName = pluginClass .getSimpleName ();
146
161
/*
147
162
* Check system properties for plugin class.
148
163
* <p>
149
164
* This will only happen during system startup thus it's okay to use the synchronized
150
165
* System.getProperties as it will never get called in normal operations.
151
166
*/
152
- String implementingClass = System .getProperty ("rxjava.plugin." + classSimpleName + ".implementation" );
167
+
168
+ final String pluginPrefix = "rxjava.plugin." ;
169
+
170
+ String defaultKey = pluginPrefix + classSimpleName + ".implementation" ;
171
+ String implementingClass = props .getProperty (defaultKey );
172
+
173
+ if (implementingClass == null ) {
174
+ final String classSuffix = ".class" ;
175
+ final String implSuffix = ".impl" ;
176
+
177
+ for (Map .Entry <Object , Object > e : props .entrySet ()) {
178
+ String key = e .getKey ().toString ();
179
+ if (key .startsWith (pluginPrefix ) && key .endsWith (classSuffix )) {
180
+ String value = e .getValue ().toString ();
181
+
182
+ if (classSimpleName .equals (value )) {
183
+ String index = key .substring (0 , key .length () - classSuffix .length ()).substring (pluginPrefix .length ());
184
+
185
+ String implKey = pluginPrefix + index + implSuffix ;
186
+
187
+ implementingClass = props .getProperty (implKey );
188
+
189
+ break ;
190
+ }
191
+ }
192
+ }
193
+ }
194
+
153
195
if (implementingClass != null ) {
154
196
try {
155
197
Class <?> cls = Class .forName (implementingClass );
@@ -165,9 +207,9 @@ private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
165
207
} catch (IllegalAccessException e ) {
166
208
throw new RuntimeException (classSimpleName + " implementation not able to be accessed: " + implementingClass , e );
167
209
}
168
- } else {
169
- return null ;
170
210
}
211
+
212
+ return null ;
171
213
}
172
214
173
215
/**
@@ -183,7 +225,7 @@ private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
183
225
public RxJavaSchedulersHook getSchedulersHook () {
184
226
if (schedulersHook .get () == null ) {
185
227
// check for an implementation from System.getProperty first
186
- Object impl = getPluginImplementationViaProperty (RxJavaSchedulersHook .class );
228
+ Object impl = getPluginImplementationViaProperty (RxJavaSchedulersHook .class , System . getProperties () );
187
229
if (impl == null ) {
188
230
// nothing set via properties so initialize with default
189
231
schedulersHook .compareAndSet (null , RxJavaSchedulersHook .getDefaultInstance ());
0 commit comments