RxJava: best practice for composing async web requests that have dependecies - callback

First, please forgive my poor English... I'll try to make the question clear...
I have an object a initially, use a to request b, then use b to request c and d, the relationship between these objects can be simplified as:
|-> c
a -> b -|
|-> d
I wrote code like:
B objB;
C objC;
D objD;
Observable.just(a)
.doOnNext((b) -> objB = b)
.map((a) -> getB(a))
.map((b) -> getC(b))
.subscribe((c) -> {
objC = c;
Observable.just(objB)
.map((b) -> getD(b))
.subscribe((d) -> objD = d);
});
How can I improve this?
Update
Thanks all for your answers.
In fact, the actual situation is more complicated. The data flow just like:
|-> e
|-> c -|-> f
a -> b -|-> d
And all the objects (b,c,d,e,f) I would like to use. I changed my code to:
class Zip {
B b;
C c;
D d;
E e;
F f;
}
Observable<B> obB = Observable.fromCallable(this::getB).cache();
Observable<C> obC = obB.map(this::getC).cache();
Observable<D> obD = obB.map(this::getD);
Observable<E> obE = obC.map(this::getE);
Observable<F> obF = obC.map(this::getF);
obB
.zipWith(obC, (b, c) -> {
// side effects
Zip zip = new Zip();
zip.b = b;
zip.c = c;
return zip;
})
.zipWith(obD, (zip, d) -> {
// side effects
zip.d = d;
return zip;
})
.zipWith(obE, (zip, e) -> {
// side effects
zip.e = e;
return zip;
})
.zipWith(obF, (zip, f) -> {
// side effects
zip.f = f;
return zip;
})
.subscribe((zip) -> { // update UI... });
Is it the right way to do this?

Suppose you have Observable definitions of a, b, c, and d:
A a = ...
Observable<B> b(A x) {...}
Observable<C> c(B x) {...}
Observable<D> d(B x) {...}
b(a)
.flatMap(x ->
c(x).zipWith(d(x), (x,y) -> combine(x,y)))
.subscribe(subscriber);

I think you could use merge operator after get B
#Test
public void dependencies(){
Observable.just("a")
.map(this::getB)
.flatMap(c-> Observable.merge(getC(c), getD(c)))
.subscribe(System.out::println);
}
String getB(String val){
return val.concat("-b");
}
Observable<String> getC(String val){
return Observable.just(val.concat("-c"));
}
Observable<String> getD(String val){
return Observable.just(val.concat("-d"));
}
If you want to learn more about RxJava here you have some examples https://github.com/politrons/reactive

How about something like this? Personally for me code is cleaner and more maintainable. This https://medium.com/#p.tournaris/rxjava-one-observable-multiple-subscribers-7bf497646675#.3apipnkx4 article might be useful.
public class SomeData {
final Observable<String> observableA;
final Observable<String> observableB;
final Observable<String> observableC;
final Observable<String> observableD;
public final BehaviorSubject<Throwable> error = BehaviorSubject.create();
public SomeData() {
observableA = Observable.fromCallable( // E.G. SOME API CALL
() -> {
// throw new RuntimeException("Some Error");
return "A";
})
.onErrorResumeNext(new handleError())
.cache();
observableB = observableA // SOME DATA MANIPULATION
.flatMap((s) -> Observable.just(s + " B"))
.onErrorResumeNext(new handleError())
.cache();
observableC = observableB // FURTHER DATA PROCESSING
.flatMap((s) -> Observable.just(s + " C"))
.onErrorResumeNext(new handleError());
observableD = observableB // FURTHER DATA PROCESSING
.flatMap((s) -> Observable.just(s + " D"))
.onErrorResumeNext(new handleError());
}
public Observable<Throwable> getError() {
return error;
}
private class handleError implements Func1<Throwable, Observable<? extends String>> {
#Override
public Observable<? extends String> call(Throwable throwable) {
return Observable.just(throwable).map(throwable1 -> {
error.onNext(throwable1);
return "some error handling here";
});
}
}
}

I think you can achieve what you want with combination of flatMap and zip operators. Here is some code:
public static void test() {
A a = new A();
a.getB()
.flatMap(b -> Observable.zip(b.getC(), b.getD(), (c, d) -> c))
.flatMap(c -> Observable.zip(c.getE(), c.getF(), (e, f) -> f))
.subscribe();
}
public static class A {
public Observable<B> getB() {
return Observable.just(new B());
}
}
public static class B {
public Observable<C> getC() {
return Observable.just(new C());
}
public Observable<D> getD() {
return Observable.just(new D())
}
}
public static class C {
public Observable<E> getE() {
return Observable.just(new E());
}
public Observable<F> getF() {
return Observable.just(new F())
}
}
public static class D {
}
public static class E {
}
public static class F {
}

Related

Translating java DFS algorithm code to Dart

I've been doing some research to find a suitable algorithm for suggesting friends. I came across DFS, but I've never implemented it in Dart before. Could someone please help me t translate it into Dart? Below is the java code:
public class SuggestFriendsDFS<T> {
private HashMap<T, ArrayList<T>> adj = new HashMap<>(); //graph
private List<Set<T>> groups = new ArrayList<>();
public void addFriendship(T src, T dest) {
adj.putIfAbsent(src, new ArrayList<T>());
adj.get(src).add(dest);
adj.putIfAbsent(dest, new ArrayList<T>());
adj.get(dest).add(src);
}
//V is total number of people, E is number of connections
private void findGroups() {
Map<T, Boolean> visited = new HashMap<>();
for (T t: adj.keySet())
visited.put(t, false);
for (T t:adj.keySet()) {
if (!visited.get(t)) {
Set<T> group = new HashSet<>();
dfs(t, visited, group);
groups.add(group);
}
}
}
//DFS + memoization
private void dfs(T v, Map<T, Boolean> visited, Set<T> group ) {
visited.put(v,true);
group.add(v);
for (T x : adj.get(v)) {
if (!visited.get(x))
dfs(x, visited, group);
}
}
public Set<T> getSuggestedFriends (T a) {
if (groups.isEmpty())
findGroups();
Set<T> res = new HashSet<>();
for (Set<T> t : groups) {
if (t.contains(a)) {
res = t;
break;
}
}
if (res.size() > 0)
res.remove(a);
return res;
}
}
I'm aware it's too much to ask, but any help will be much appreciated as I tried to translate it and ended up getting loads of errors. Thanks in advance!(: For reference, this is where I found the explanation for the java code.
I tried https://sma.github.io/stuff/java2dartweb/java2dartweb.html that does automatic Java to Dart conversion but it doesn't work well as soon as the code is a bit complex.
See the full conversion below, you can try it in Dartpad
import 'dart:collection';
class SuggestFriendsDFS<T> {
final HashMap<T, List<T>> _adj = HashMap(); //graph
final List<Set<T>> groups = [];
//Time O(1), Space O(1)
void addFriendship(T src, T dest) {
_adj.putIfAbsent(src, () => <T>[]);
_adj[src]!.add(dest);
_adj.putIfAbsent(dest, () => <T>[]);
_adj[dest]!.add(src);
}
//DFS wrapper, Time O(V+E), Space O(V)
//V is total number of people, E is number of connections
void findGroups() {
Map<T, bool> visited = HashMap();
for (T t in _adj.keys) {
visited[t] = false;
}
for (T t in _adj.keys) {
if (visited[t] == false) {
Set<T> group = HashSet();
_dfs(t, visited, group);
groups.add(group);
}
}
}
//DFS + memoization, Time O(V+E), Space O(V)
void _dfs(T v, Map<T, bool> visited, Set<T> group) {
visited[v] = true;
group.add(v);
for (T x in _adj[v] ?? []) {
if ((visited[x] ?? true) == false) _dfs(x, visited, group);
}
}
//Time O(V+E), Space O(V)
Set<T> getSuggestedFriends(T a) {
if (groups.isEmpty) findGroups();
var result = groups.firstWhere((element) => element.contains(a),
orElse: () => <T>{});
if (result.isNotEmpty) result.remove(a);
return result;
}
}
void main() {
SuggestFriendsDFS<String> g = SuggestFriendsDFS();
g.addFriendship("Ashley", "Christopher");
g.addFriendship("Ashley", "Emily");
g.addFriendship("Ashley", "Joshua");
g.addFriendship("Bart", "Lisa");
g.addFriendship("Bart", "Matthew");
g.addFriendship("Christopher", "Andrew");
g.addFriendship("Emily", "Joshua");
g.addFriendship("Jacob", "Christopher");
g.addFriendship("Jessica", "Ashley");
g.addFriendship("JorEl", "Zod");
g.addFriendship("KalEl", "JorEl");
g.addFriendship("Kyle", "Lex");
g.addFriendship("Kyle", "Zod");
g.addFriendship("Lisa", "Marge");
g.addFriendship("Matthew", "Lisa");
g.addFriendship("Michael", "Christopher");
g.addFriendship("Michael", "Joshua");
g.addFriendship("Michael", "Jessica");
g.addFriendship("Samantha", "Matthew");
g.addFriendship("Samantha", "Tyler");
g.addFriendship("Sarah", "Andrew");
g.addFriendship("Sarah", "Christopher");
g.addFriendship("Sarah", "Emily");
g.addFriendship("Tyler", "Kyle");
g.addFriendship("Stuart", "Jacob");
g.findGroups();
print(g.groups);
String name = "Andrew";
print("Suggestion friends of " +
name +
": " +
g.getSuggestedFriends(name).toString());
}

Converting event based API to Rx.Net

I'm trying to convert an existing event-based API to a Reactive Observable API. The concrete API I'm working with is the NSNetServiceBrowser in Xamarin.iOS. This API let you browse for network devices using Zeroconf/Bonjour. However, the question would apply to any API of this kind.
The NsNetServiceBrowser offers various events of interest:
- FoundService
- NotSearched
- ServiceRemoved
The FoundService event is raised when a service is discovered, and the NotSearched is raised when the search fails.
I would like to combine the FoundService and NotSerched events, into an observable of NSNetService.
My current implementation looks like this:
public IObservable<NSNetService> Search()
{
var foundObservable = Observable
.FromEventPattern<NSNetServiceEventArgs>(
h => serviceBrowser.FoundService += h,
h => serviceBrowser.FoundService -= h)
.Select(x => x.EventArgs);
var notSearchedObservable = Observable
.FromEventPattern<NSNetServiceErrorEventArgs>(
h => serviceBrowser.NotSearched += h,
h => serviceBrowser.NotSearched -= h)
.Select(x => x.EventArgs);
var serviceObservable = Observable.Create(
(IObserver<NSNetServiceEventArgs> observer) =>
{
notSearchedObservable.Subscribe(n =>
{
string errorMessage = $"Search for {serviceType} failed:";
foreach (var kv in n.Errors)
{
log.Error($"\t{kv.Key}: {kv.Value}");
errorMessage += $" ({kv.Key}, {kv.Value})";
}
observer.OnError(new Exception(errorMessage));
});
foundObservable.Subscribe(observer);
return System.Reactive.Disposables.Disposable.Empty;
}).Select(x => x.Service);
serviceBrowser.SearchForServices(serviceType, domain);
return serviceObservable;
}
The code looks clunky and I have a gut feeling I'm not using System.Reactive correctly? Is there a more elegant way to combine event pairs, where one is producing and the other is signaling error? This is a common pattern in existing event based APIs in .NET.
Here is a small console app (depending only on System.Reactive) illustrating the type of API I want to Reactify:
using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
namespace ReactiveLearning
{
class Program
{
static void Main(string[] args)
{
var browser = new ServiceBrowser();
var observableFound =
Observable.FromEventPattern<ServiceFoundEventArgs>(
h => browser.ServiceFound += h,
h => browser.ServiceFound -= h)
.Select(e => e.EventArgs.Service);
var observableError =
Observable.FromEventPattern<ServiceSearchErrorEventArgs>(
h => browser.ServiceError += h,
h => browser.ServiceError -= h);
var foundSub = observableFound.Subscribe(s =>
{
Console.WriteLine($"Found service: {s.Name}");
}, () =>
{
Console.WriteLine("Found Completed");
});
var errorSub = observableError.Subscribe(e =>
{
Console.WriteLine("ERROR!");
}, () =>
{
Console.WriteLine("Error Completed");
});
browser.Search();
Console.ReadLine();
foundSub.Dispose();
errorSub.Dispose();
Console.WriteLine();
}
}
class ServiceBrowser
{
public EventHandler<ServiceFoundEventArgs> ServiceFound;
public EventHandler<ServiceSearchErrorEventArgs> ServiceError;
public void Search()
{
Task.Run(async () =>
{
for (var i = 0; i < 5; ++i)
{
await Task.Delay(1000);
ServiceFound?.Invoke(this, new ServiceFoundEventArgs(new Service($"Service {i}")));
}
var r = new Random();
if (r.NextDouble() > 0.5)
{
ServiceError?.Invoke(this, new ServiceSearchErrorEventArgs());
}
});
}
}
class ServiceFoundEventArgs : EventArgs
{
public Service Service { get; private set; }
public ServiceFoundEventArgs(Service service) => Service = service;
}
class ServiceSearchErrorEventArgs : EventArgs {}
class Service
{
public event EventHandler<EventArgs> AddressResolved;
public event EventHandler<EventArgs> ErrorResolvingAddress;
public string Name { get; private set; }
public string Address { get; private set; }
public Service(string name) => Name = name;
public void ResolveAddress()
{
Task.Run(async () =>
{
await Task.Delay(500);
var r = new Random();
if (r.NextDouble() > 0.5)
{
Address = $"http://{Name}.com";
AddressResolved?.Invoke(this, EventArgs.Empty);
}
else
{
ErrorResolvingAddress?.Invoke(this, EventArgs.Empty);
}
});
}
}
}
Thank you for the excellent sample code. You need to make use of the excellent Materialize & Dematerialize operators. Here's how:
var observableFoundWithError =
observableFound
.Materialize()
.Merge(
observableError
.Materialize()
.Select(x =>
Notification
.CreateOnError<Service>(new Exception("Error"))))
.Dematerialize()
.Synchronize();
using (observableFoundWithError.Subscribe(
s => Console.WriteLine($"Found service: {s.Name}"),
ex => Console.WriteLine($"Found error: {ex.Message}"),
() => Console.WriteLine("Found Completed")))
{
browser.Search();
Console.ReadLine();
}
The Materialize() operator turns an IObservable<T> into and IObservable<Notification<T>> which allows the standard OnError and OnCompleted to be emitted through the OnNext call. You can use Notification.CreateOnError<T>(new Exception("Error")) to construct elements of observable which you can turn back into an IObservable<T> with Dematerialize().
I've thrown the Synchronize() to ensure that you've created a valid observable. The use of Materialize() does let you construct observables that don't follow the regular observable contract. Part of what Synchronize() does is just ensure only one OnError and only one OnCompleted and drops any OnNext that comes after either of the two.
Try this as a way to do what you wanted in the comments:
static void Main(string[] args)
{
var browser = new ServiceBrowser();
var observableFound =
Observable.FromEventPattern<ServiceFoundEventArgs>(
h => browser.ServiceFound += h,
h => browser.ServiceFound -= h)
.Select(e => e.EventArgs.Service);
var observableError =
Observable.FromEventPattern<ServiceSearchErrorEventArgs>(
h => browser.ServiceError += h,
h => browser.ServiceError -= h);
var observableFoundWithError = observableFound
.Materialize()
.Merge(
observableError
.Materialize()
.Select(x => Notification.CreateOnError<Service>(new Exception("Error"))))
.Dematerialize()
.Synchronize();
Func<Service, IObservable<Service>> resolveService = s =>
Observable.Create<Service>(o =>
{
var observableResolved = Observable.FromEventPattern<EventArgs>(
h => s.AddressResolved += h,
h => s.AddressResolved -= h);
var observableResolveError = Observable.FromEventPattern<EventArgs>(
h => s.ErrorResolvingAddress += h,
h => s.ErrorResolvingAddress -= h);
var observableResolvedWithError =
observableResolved
.Select(x => s)
.Materialize()
.Merge(
observableResolveError
.Do(e => Console.WriteLine($"Error resolving: {s.Name}"))
.Materialize()
.Select(x => Notification.CreateOnError<Service>(new Exception($"Error resolving address for service: {s.Name}"))))
.Dematerialize()
.Synchronize();
s.ResolveAddress();
return observableResolvedWithError.Subscribe(o);
});
using (
observableFoundWithError
.Select(s => resolveService(s))
.Switch()
.Subscribe(
s => Console.WriteLine($"Found and resolved service: {s.Name} ({s.Address})"),
ex => Console.WriteLine($"Found error: {ex.Message}"),
() => Console.WriteLine("Found Completed")))
{
browser.Search();
Console.ReadLine();
}
}
public class ServiceBrowser
{
public event EventHandler<ServiceFoundEventArgs> ServiceFound;
public event EventHandler<ServiceSearchErrorEventArgs> ServiceError;
public void Search() { }
}
public class Service
{
public event EventHandler<EventArgs> AddressResolved;
public event EventHandler<EventArgs> ErrorResolvingAddress;
public string Name;
public string Address;
public void ResolveAddress() { }
}
public class ServiceFoundEventArgs : EventArgs
{
public Service Service;
}
public class ServiceSearchErrorEventArgs : EventArgs
{
}
I might require a bit of tweaking - perhaps an Observable.Delay in there. Let me know if it works.

Is there an equivalent of Project Reactor's Flux.create() that caters for push/pull model in rxjava-2?

Project Reactor has this factory method for creating a push/pull Producer<T>.
http://projectreactor.io/docs/core/release/reference/#_hybrid_push_pull_model
Is there any such thing in RxJava-2?
If not, what would be the recommended way (without actually implemementing reactive specs interfaces from scratch) to create such beast that can handle the push/pull model?
EDIT: as requested I am giving an example of the API I am trying to use...
private static class API
{
CompletableFuture<Void> getT(Consumer<Object> consumer) {}
}
private static class Callback implements Consumer<Object>
{
private API api;
public Callback(API api) { this api = api; }
#Override
public void accept(Object o)
{
//do stuff with o
//...
//request for another o
api.getT(this);
}
}
public void example()
{
API api = new API();
api.getT(new Callback(api)).join();
}
So it's call back based, which will get one item and from within you can request for another one. the completable future flags no more items.
Here is an example of a custom Flowable that turns this particular API into an RxJava source. Note however that in general, the API peculiarities in general may not be possible to capture with a single reactive bridge design:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import org.reactivestreams.*;
import io.reactivex.Flowable;
import io.reactivex.internal.subscriptions.EmptySubscription;
import io.reactivex.internal.util.BackpressureHelper;
public final class SomeAsyncApiBridge<T> extends Flowable<T> {
final Function<? super Consumer<? super T>,
? extends CompletableFuture<Void>> apiInvoker;
final AtomicBoolean once;
public SomeAsyncApiBridge(Function<? super Consumer<? super T>,
? extends CompletableFuture<Void>> apiInvoker) {
this.apiInvoker = apiInvoker;
this.once = new AtomicBoolean();
}
#Override
protected void subscribeActual(Subscriber<? super T> s) {
if (once.compareAndSet(false, true)) {
SomeAsyncApiBridgeSubscription<T> parent =
new SomeAsyncApiBridgeSubscription<>(s, apiInvoker);
s.onSubscribe(parent);
parent.moveNext();
} else {
EmptySubscription.error(new IllegalStateException(
"Only one Subscriber allowed"), s);
}
}
static final class SomeAsyncApiBridgeSubscription<T>
extends AtomicInteger
implements Subscription, Consumer<T>, BiConsumer<Void, Throwable> {
/** */
private static final long serialVersionUID = 1270592169808316333L;
final Subscriber<? super T> downstream;
final Function<? super Consumer<? super T>,
? extends CompletableFuture<Void>> apiInvoker;
final AtomicInteger wip;
final AtomicLong requested;
final AtomicReference<CompletableFuture<Void>> task;
static final CompletableFuture<Void> TASK_CANCELLED =
CompletableFuture.completedFuture(null);
volatile T item;
volatile boolean done;
Throwable error;
volatile boolean cancelled;
long emitted;
SomeAsyncApiBridgeSubscription(
Subscriber<? super T> downstream,
Function<? super Consumer<? super T>,
? extends CompletableFuture<Void>> apiInvoker) {
this.downstream = downstream;
this.apiInvoker = apiInvoker;
this.requested = new AtomicLong();
this.wip = new AtomicInteger();
this.task = new AtomicReference<>();
}
#Override
public void request(long n) {
BackpressureHelper.add(requested, n);
drain();
}
#Override
public void cancel() {
cancelled = true;
CompletableFuture<Void> curr = task.getAndSet(TASK_CANCELLED);
if (curr != null && curr != TASK_CANCELLED) {
curr.cancel(true);
}
if (getAndIncrement() == 0) {
item = null;
}
}
void moveNext() {
if (wip.getAndIncrement() == 0) {
do {
CompletableFuture<Void> curr = task.get();
if (curr == TASK_CANCELLED) {
return;
}
CompletableFuture<Void> f = apiInvoker.apply(this);
if (task.compareAndSet(curr, f)) {
f.whenComplete(this);
} else {
curr = task.get();
if (curr == TASK_CANCELLED) {
f.cancel(true);
return;
}
}
} while (wip.decrementAndGet() != 0);
}
}
#Override
public void accept(Void t, Throwable u) {
if (u != null) {
error = u;
task.lazySet(TASK_CANCELLED);
}
done = true;
drain();
}
#Override
public void accept(T t) {
item = t;
drain();
}
void drain() {
if (getAndIncrement() != 0) {
return;
}
int missed = 1;
long e = emitted;
for (;;) {
for (;;) {
if (cancelled) {
item = null;
return;
}
boolean d = done;
T v = item;
boolean empty = v == null;
if (d && empty) {
Throwable ex = error;
if (ex == null) {
downstream.onComplete();
} else {
downstream.onError(ex);
}
return;
}
if (empty || e == requested.get()) {
break;
}
item = null;
downstream.onNext(v);
e++;
moveNext();
}
emitted = e;
missed = addAndGet(-missed);
if (missed == 0) {
break;
}
}
}
}
}
Test and example source:
import java.util.concurrent.*;
import java.util.function.Consumer;
import org.junit.Test;
public class SomeAsyncApiBridgeTest {
static final class AsyncRange {
final int max;
int index;
public AsyncRange(int start, int count) {
this.index = start;
this.max = start + count;
}
public CompletableFuture<Void> next(Consumer<? super Integer> consumer) {
int i = index;
if (i == max) {
return CompletableFuture.completedFuture(null);
}
index = i + 1;
CompletableFuture<Void> cf = CompletableFuture
.runAsync(() -> consumer.accept(i));
CompletableFuture<Void> cancel = new CompletableFuture<Void>() {
#Override
public boolean cancel(boolean mayInterruptIfRunning) {
cf.cancel(mayInterruptIfRunning);
return super.cancel(mayInterruptIfRunning);
}
};
return cancel;
}
}
#Test
public void simple() {
AsyncRange r = new AsyncRange(1, 10);
new SomeAsyncApiBridge<Integer>(
consumer -> r.next(consumer)
)
.test()
.awaitDone(500, TimeUnit.SECONDS)
.assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
}
This is something that looks that is working using Reactor's Flux.create(). I changed the API a bit.
public class FlowableGenerate4
{
private static class API
{
private ExecutorService es = Executors.newFixedThreadPool(1);
private CompletableFuture<Void> done = new CompletableFuture<>();
private AtomicInteger stopCounter = new AtomicInteger(10);
public boolean isDone()
{
return done.isDone();
}
public CompletableFuture<Void> getT(Consumer<Object> consumer)
{
es.submit(() -> {
try {
Thread.sleep(100);
} catch (Exception e) {
}
if (stopCounter.decrementAndGet() < 0)
done.complete(null);
else
consumer.accept(new Object());
});
return done;
}
}
private static class Callback implements Consumer<Object>
{
private API api;
private FluxSink<Object> sink;
public Callback(API api, FluxSink<Object> sink)
{
this.api = api;
this.sink = sink;
}
#Override
public void accept(Object o)
{
sink.next(o);
if (sink.requestedFromDownstream() > 0 && !api.isDone())
api.getT(this);
else
sink.currentContext().<AtomicBoolean>get("inProgress")
.set(false);
}
}
private Publisher<Object> reactorPublisher()
{
API api = new API();
return
Flux.create(sink -> {
sink.onRequest(n -> {
//if it's in progress already, do nothing
//I understand that onRequest() can be called asynchronously
//regardless if the previous call demand has been satisfied or not
if (!sink.currentContext().<AtomicBoolean>get("inProgress")
.compareAndSet(false, true))
return;
//else kick off calls to API
api.getT(new Callback(api, sink))
.whenComplete((o, t) -> {
if (t != null)
sink.error(t);
else
sink.complete();
});
});
}).subscriberContext(
Context.empty().put("inProgress", new AtomicBoolean(false)));
}
#Test
public void test()
{
Flowable.fromPublisher(reactorPublisher())
.skip(5)
.take(10)
.blockingSubscribe(
i -> System.out.println("onNext()"),
Throwable::printStackTrace,
() -> System.out.println("onComplete()")
);
}
}

How implement akka actor in functional style with java

I have simple counter actor implemented in java:
public class CounterJavaActor extends UntypedActor {
int count = 0;
#Override
public void onReceive(Object message) throws Exception {
if (message.equals("incr")) {
count += 1;
} else if (message.equals("get")) {
sender().tell(count, self());
}
}
}
In courses on coursera "Functional reactive programming in scala", I saw functional impementation of counter:
/**
* Advantages:
* state change is explicit
* state is scoped to current behaviour
*/
class CounterScala extends Actor{
def counter(n: Int) : Receive = {
case "incr" => context.become(counter(n+1))
case "get" => sender ! n
}
def receive = counter(0)
}
Upd:
My problem, that in java i can't make recourse functional call like in scala counter(n+1). What it means:
public class CounterJava8Actor extends AbstractActor {
//counter(0) in scala
private PartialFunction<Object, BoxedUnit> counter;
private int n = 0;
public CounterJava8Actor() {
counter =
ReceiveBuilder.
matchEquals("get", s -> {
sender().tell(n, self());
}).
matchEquals("inc", s -> {
//become(counter(n+1) in scala
context().become(counter);
}).build();
receive(counter);
}
}
It is possible to implement it in functional style with java?
According to docs you can use become/unbecome in java 8
http://doc.akka.io/docs/akka/snapshot/java/lambda-actors.html#become-unbecome
here is the sample code copied from there
public class HotSwapActor extends AbstractActor {
private PartialFunction<Object, BoxedUnit> angry;
private PartialFunction<Object, BoxedUnit> happy;
public HotSwapActor() {
angry =
ReceiveBuilder.
matchEquals("foo", s -> {
sender().tell("I am already angry?", self());
}).
matchEquals("bar", s -> {
context().become(happy);
}).build();
happy = ReceiveBuilder.
matchEquals("bar", s -> {
sender().tell("I am already happy :-)", self());
}).
matchEquals("foo", s -> {
context().become(angry);
}).build();
receive(ReceiveBuilder.
matchEquals("foo", s -> {
context().become(angry);
}).
matchEquals("bar", s -> {
context().become(happy);
}).build()
);
}
}
Or you can use UntypedActor like explained in the docs here
http://doc.akka.io/docs/akka/snapshot/java/untyped-actors.html
public class Manager extends UntypedActor {
public static final String SHUTDOWN = "shutdown";
ActorRef worker = getContext().watch(getContext().actorOf(
Props.create(Cruncher.class), "worker"));
public void onReceive(Object message) {
if (message.equals("job")) {
worker.tell("crunch", getSelf());
} else if (message.equals(SHUTDOWN)) {
worker.tell(PoisonPill.getInstance(), getSelf());
getContext().become(shuttingDown);
}
}
Procedure<Object> shuttingDown = new Procedure<Object>() {
#Override
public void apply(Object message) {
if (message.equals("job")) {
getSender().tell("service unavailable, shutting down", getSelf());
} else if (message instanceof Terminated) {
getContext().stop(getSelf());
}
}
};
}
To know how to add parameter to Procedure you can see this answer:
Akka/Java getContext().become with parameter?
and here is actual solution with java 8
private PartialFunction<Object, BoxedUnit> counter(final int n) {
return ReceiveBuilder.
matchEquals("get", s -> {
sender().tell(n, self());
}).
matchEquals("inc", s -> {
context().become(counter(n + 1));
}).build();
}
public CounterJava8Actor() {
receive(counter(0));
}

Implementing resource queue in rx

I have a hot observable Observable<Resource> resources that represents consumable resources and I want to queue up consumers Action1<Resource> for these resources. A Resource can be used by at most 1 consumer. It should not be used at all once a new value is pushed from resources. If my consumers were also wrapped in a hot observable then the marble-diagram of what I'm after would be
--A--B--C--D--E--
----1----2--34---
----A----C--D-E--
----1----2--3-4--
I've managed a naive implementation using a PublishSubject and zip but this only works if each resource is consumed before a new resource is published (i.e. instead of the required sequence [A1, C2, D3, E4] this implementation will actually produce [A1, B2, C3, D4]).
This is my first attempt at using rx and I've had a play around with both delay and join but can't quite seem to get what I'm after. I've also read that ideally Subjects should be avoided, but I can't see how else I would implement this.
public class ResourceQueue<Resource> {
private final PublishSubject<Action1<Resource>> consumers = PublishSubject.create();
public ResourceQueue(Observable<Resource> resources) {
resources.zipWith(this.consumers, new Func2<Resource, Action1<Resource>, Object>() {
#Override
public Object call(Resource resource, Action1<Resource> consumer) {
consumer.execute(resource);
return null;
}
}).publish().connect();
}
public void queue(final Action1<Resource> consumer) {
consumers.onNext(consumer);
}
}
Is there a way to achieve what I'm after? Is there a more 'rx-y' approach to the solution?
EDIT: changed withLatesFrom suggestion with combineLatest.
The only solution I can think of is to use combineLatest to get all the possible combinations, and manually exclude the ones that you do not need:
final ExecutorService executorService = Executors.newCachedThreadPool();
final Observable<String> resources = Observable.create(s -> {
Runnable r = new Runnable() {
#Override
public void run() {
final List<Integer> sleepTimes = Arrays.asList(200, 200, 200, 200, 200);
for (int i = 0; i < sleepTimes.size(); i++) {
try {
Thread.sleep(sleepTimes.get(i));
} catch (Exception e) {
e.printStackTrace();
}
String valueOf = String.valueOf((char) (i + 97));
System.out.println("new resource " + valueOf);
s.onNext(valueOf);
}
s.onCompleted();
}
};
executorService.submit(r);
});
final Observable<Integer> consumers = Observable.create(s -> {
Runnable r = new Runnable() {
#Override
public void run() {
final List<Integer> sleepTimes = Arrays.asList(300, 400, 200, 0);
for (int i = 0; i < sleepTimes.size(); i++) {
try {
Thread.sleep(sleepTimes.get(i));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("new consumer " + (i + 1));
s.onNext(i + 1);
}
s.onCompleted();
};
};
executorService.submit(r);
});
final LatestValues latestValues = new LatestValues();
final Observable<String> combineLatest = Observable.combineLatest(consumers, resources, (c, r) -> {
if (latestValues.alreadyProcessedAnyOf(c, r)) {
return "";
}
System.out.println("consumer " + c + " will consume resource " + r);
latestValues.updateWithValues(c, r);
return c + "_" + r;
});
combineLatest.subscribe();
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.SECONDS);
The class holding the latest consumers and resources.
static class LatestValues {
Integer latestConsumer = Integer.MAX_VALUE;
String latestResource = "";
public boolean alreadyProcessedAnyOf(Integer c, String r) {
return latestConsumer.equals(c) || latestResource.equals(r);
}
public void updateWithValues(Integer c, String r) {
latestConsumer = c;
latestResource = r;
}
}