3

I'm new to RxJava, and I am wondering how I can create a configurable Observable? Let's imagine I could write a DB-to-DB transfer like this:

srcDb.getObservable(Bean.class)
     .sql(selectSql)
     .params(selectParams)
     .subscribe(
          trgDb.getSubscriber(Bean.class)
               .sql(insertSql)
     );

I can already do that with the Subscriber, but how can I get some small configuration in the same fashion to the Observable itself?

Obito
  • 391
  • 3
  • 8
  • You cannot that easily add your own method in the core Rx API, however what you can do is writing your own builders with that same kind of fluent-writing style, and make those builders return an observable at the end. Doing this, you'll be able to keep this fluent writing. – Joel Oct 03 '16 at 17:18

2 Answers2

2

There's 2 ways you can do that:

Option #1: have your own objects do the configuration, and then have an execute(), query() or toObservable() that switches domains:

 srcDb
 .find(Bean.class)
 .sql(selectSql)
 .params(selectParams)
 .execute()
 .subscribe(
      trgDb.getSubscriber(Bean.class)
           .sql(insertSql)
 );

Option #2: use .compose() to re-use common operations:

srcDb
.getObservable(Bean.class)
.compose(addSQLParameters())
.subscribe(
      trgDb.getSubscriber(Bean.class)
           .sql(insertSql)
 );

 <T> Transformer<T,T> addSQLParameters() {
   return obs -> obs.sql(selectSql).params(selectParams);
 }

I would suggest you use option #1, as it allows much better management of your part of the code.

Tassos Bassoukos
  • 16,017
  • 2
  • 36
  • 40
  • You and Joel (with his comment) are probably right. I tried to do some experiments using reflection to expose the onSubscribe field from the parent class, which actually allowed me to do exactly what I want, but it's not a clean solution :-). Anyway it would be nice if RxJava provided some version of Observable with the getOnSubscribe() method for developers claiming that they "know what they're doing" :-D. – LittleLight Oct 04 '16 at 15:57
  • You might be interested in `RxJavaHooks#setOnObservableCreate` - but keep in mind that applies to *all* observables. IMO you're having an XY problem - just do the configuration before you create an observable. – Tassos Bassoukos Oct 04 '16 at 16:24
  • Thanks, I will look into it :-). – LittleLight Oct 04 '16 at 16:43
0

Maybe I found an acceptable way around this. It seems that what I need to do here is a double-binding outside of the Observable instantiation itself. E.g. I need a DbObservable and DbOnSubscribe pair which is counting on each other, something like this:

DbObservable class:

public class DbObservable<T> extends Observable<T> {

    //Some parameter
    private String sql;

    protected DbObservable(DbOnSubscribe<T> onSub) {
        super(onSub);
    }

    //Getter for DbOnSubscribe
    public String getSql() {
        return sql;
    }

    //Chain parameter modifier
    public DbObservable<T> sql(String sql) {
        this.sql = sql;
        return this;
    }
}

DbOnSubscribe class:

public class DbOnSubscribe<T> implements Observable.OnSubscribe<T> {

    private DbObservable<T> dbObservable;

    @Override
    public void call(Subscriber<? super T> subscriber) {
        String sql = dbObservable.getSql(); //Access SQL param
        subscriber.onNext( (T) sql ); //Use subscriber
        subscriber.onCompleted();
    }

    //Set back-reference
    public void setDbObservable(DbObservable<T> dbObservable) {
        this.dbObservable = dbObservable;
    }
}

And finally our assumed DbConnector class:

public class DbConnector {

    public DbObservable<String> getObservable() {
        DbOnSubscribe<String> onSub = new DbOnSubscribe<String>();
        DbObservable<String> obs = new DbObservable<>(onSub);
        onSub.setDbObservable(obs);
        return obs;
    }
}

So when I try it out ...

public class DbObservableTest {

    public static void main(String[] args) {
        DbConnector srcDb = new DbConnector();

        srcDb.getObservable()
                .sql("some SQL")
                .subscribe(System.out::println);
    }
}

... it really works! It prints out the "some SQL".

Conclusion

  1. If you want to be super-clean and don't mind one or 2 extra lines of code, go for a builder as proposed by Joel and Tassos Bassoukos.
  2. If you're not afraid of a little bit more complicated code (which should be always encapsulated somewhere) and you really want those parameters to be inside your own Observable, you can try the double-binding way
  3. Any more options?