Change the license to GPL v3 to be compatible with Apache License 2.0.

This commit is contained in:
huanghongxun
2015-12-05 21:41:50 +08:00
parent f0212ea4eb
commit d3ad805ad3
331 changed files with 12761 additions and 1878 deletions

View File

@@ -0,0 +1,161 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx;
/**
* An object representing a notification sent to an {@link Observable}.
*
* For the Microsoft Rx equivalent see: http://msdn.microsoft.com/en-us/library/hh229462(v=vs.103).aspx
*/
public class Notification<T> {
private final Kind kind;
private final Exception exception;
private final T value;
/**
* A constructor used to represent an onNext notification.
*
* @param value
* The data passed to the onNext method.
*/
public Notification(T value) {
this.value = value;
this.exception = null;
this.kind = Kind.OnNext;
}
/**
* A constructor used to represent an onError notification.
*
* @param exception
* The exception passed to the onError notification.
*/
public Notification(Exception exception) {
this.exception = exception;
this.value = null;
this.kind = Kind.OnError;
}
/**
* A constructor used to represent an onCompleted notification.
*/
public Notification() {
this.exception = null;
this.value = null;
this.kind = Kind.OnCompleted;
}
/**
* Retrieves the exception associated with an onError notification.
*
* @return The exception associated with an onError notification.
*/
public Exception getException() {
return exception;
}
/**
* Retrieves the data associated with an onNext notification.
*
* @return The data associated with an onNext notification.
*/
public T getValue() {
return value;
}
/**
* Retrieves a value indicating whether this notification has a value.
*
* @return a value indicating whether this notification has a value.
*/
public boolean hasValue() {
return isOnNext() && value != null;
}
/**
* Retrieves a value indicating whether this notification has an exception.
*
* @return a value indicating whether this notification has an exception.
*/
public boolean hasException() {
return isOnError() && exception != null;
}
/**
* The kind of notification: OnNext, OnError, OnCompleted
*
* @return
*/
public Kind getKind() {
return kind;
}
public boolean isOnError() {
return getKind() == Kind.OnError;
}
public boolean isOnCompleted() {
return getKind() == Kind.OnCompleted;
}
public boolean isOnNext() {
return getKind() == Kind.OnNext;
}
public static enum Kind {
OnNext, OnError, OnCompleted
}
@Override
public String toString() {
StringBuilder str = new StringBuilder("[").append(super.toString()).append(" ").append(getKind());
if (hasValue())
str.append(" ").append(getValue());
if (hasException())
str.append(" ").append(getException().getMessage());
str.append("]");
return str.toString();
}
@Override
public int hashCode() {
int hash = getKind().hashCode();
if (hasValue())
hash = hash * 31 + getValue().hashCode();
if (hasException())
hash = hash * 31 + getException().hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (this == obj)
return true;
if (obj.getClass() != getClass())
return false;
Notification<?> notification = (Notification<?>) obj;
if (notification.getKind() != getKind())
return false;
if (hasValue() && !getValue().equals(notification.getValue()))
return false;
if (hasException() && !getException().equals(notification.getException()))
return false;
return true;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx;
/**
* Provides a mechanism for receiving push-based notifications.
* <p>
* After an Observer calls an {@link Observable}'s <code>Observable.subscribe</code> method, the {@link Observable} calls the Observer's <code>onNext</code> method to provide notifications. A
* well-behaved {@link Observable} will
* call an Observer's <code>onCompleted</code> closure exactly once or the Observer's <code>onError</code> closure exactly once.
* <p>
* For more information see the <a href="https://github.com/Netflix/RxJava/wiki/Observable">RxJava Wiki</a>
*
* @param <T>
*/
public interface Observer<T> {
/**
* Notifies the Observer that the {@link Observable} has finished sending push-based notifications.
* <p>
* The {@link Observable} will not call this closure if it calls <code>onError</code>.
*/
public void onCompleted();
/**
* Notifies the Observer that the {@link Observable} has experienced an error condition.
* <p>
* If the {@link Observable} calls this closure, it will not thereafter call <code>onNext</code> or <code>onCompleted</code>.
*
* @param e
*/
public void onError(Exception e);
/**
* Provides the Observer with new data.
* <p>
* The {@link Observable} calls this closure 1 or more times, unless it calls <code>onError</code> in which case this closure may never be called.
* <p>
* The {@link Observable} will not call this closure again after it calls either <code>onCompleted</code> or <code>onError</code>.
*
* @param args
*/
public void onNext(T args);
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx;
import java.util.concurrent.TimeUnit;
import rx.util.functions.Action0;
import rx.util.functions.Func0;
/**
* Represents an object that schedules units of work.
*/
public interface Scheduler {
/**
* Schedules a cancelable action to be executed.
*
* @param action
* action
* @return a subscription to be able to unsubscribe from action.
*/
Subscription schedule(Func0<Subscription> action);
/**
* Schedules an action to be executed.
*
* @param action
* action
* @return a subscription to be able to unsubscribe from action.
*/
Subscription schedule(Action0 action);
/**
* Schedules an action to be executed in dueTime.
*
* @param action
* action
* @return a subscription to be able to unsubscribe from action.
*/
Subscription schedule(Action0 action, long dueTime, TimeUnit unit);
/**
* Schedules a cancelable action to be executed in dueTime.
*
* @param action
* action
* @return a subscription to be able to unsubscribe from action.
*/
Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit);
/**
* Returns the scheduler's notion of current time.
*/
long now();
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx;
import rx.subscriptions.Subscriptions;
/**
* Subscription returns from {@link Observable#subscribe(Observer)} to allow unsubscribing.
* <p>
* See utilities in {@link Subscriptions} and implementations in the {@link rx.subscriptions} package.
*/
public interface Subscription {
/**
* Stop receiving notifications on the {@link Observer} that was registered when this Subscription was received.
* <p>
* This allows unregistering an {@link Observer} before it has finished receiving all events (ie. before onCompleted is called).
*/
public void unsubscribe();
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.concurrent.TimeUnit;
import rx.Scheduler;
import rx.Subscription;
import rx.subscriptions.Subscriptions;
import rx.util.functions.Action0;
import rx.util.functions.Func0;
/* package */abstract class AbstractScheduler implements Scheduler {
@Override
public Subscription schedule(Action0 action) {
return schedule(asFunc0(action));
}
@Override
public Subscription schedule(Action0 action, long dueTime, TimeUnit unit) {
return schedule(asFunc0(action), dueTime, unit);
}
@Override
public long now() {
return System.nanoTime();
}
private static Func0<Subscription> asFunc0(final Action0 action) {
return new Func0<Subscription>() {
@Override
public Subscription call() {
action.call();
return Subscriptions.empty();
}
};
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import rx.Subscription;
import rx.util.functions.Func0;
/**
* Schedules work on the current thread but does not execute immediately. Work is put in a queue and executed after the current unit of work is completed.
*/
public class CurrentThreadScheduler extends AbstractScheduler {
private static final CurrentThreadScheduler INSTANCE = new CurrentThreadScheduler();
public static CurrentThreadScheduler getInstance() {
return INSTANCE;
}
private static final ThreadLocal<Queue<DiscardableAction>> QUEUE = new ThreadLocal<>();
private CurrentThreadScheduler() {
}
@Override
public Subscription schedule(Func0<Subscription> action) {
DiscardableAction discardableAction = new DiscardableAction(action);
enqueue(discardableAction);
return discardableAction;
}
@Override
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
return schedule(new SleepingAction(action, this, dueTime, unit));
}
private void enqueue(DiscardableAction action) {
Queue<DiscardableAction> queue = QUEUE.get();
boolean exec = queue == null;
if (exec) {
queue = new LinkedList<>();
QUEUE.set(queue);
}
queue.add(action);
if (exec) {
while (!queue.isEmpty()) {
queue.poll().call();
}
QUEUE.set(null);
}
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func0;
/**
* Combines standard {@link Subscription#unsubscribe()} functionality with ability to skip execution if an unsubscribe occurs before the {@link #call()} method is invoked.
*/
/* package */class DiscardableAction implements Func0<Subscription>, Subscription {
private final Func0<Subscription> underlying;
private final AtomicObservableSubscription wrapper = new AtomicObservableSubscription();
private final AtomicBoolean ready = new AtomicBoolean(true);
public DiscardableAction(Func0<Subscription> underlying) {
this.underlying = underlying;
}
@Override
public Subscription call() {
if (ready.compareAndSet(true, false)) {
Subscription subscription = underlying.call();
wrapper.wrap(subscription);
return subscription;
}
return wrapper;
}
@Override
public void unsubscribe() {
ready.set(false);
wrapper.unsubscribe();
}
}

View File

@@ -0,0 +1,126 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import rx.Scheduler;
import rx.Subscription;
import rx.util.functions.Func0;
/**
* A {@link Scheduler} implementation that uses an {@link Executor} or {@link ScheduledExecutorService} implementation.
* <p>
* Note that if an {@link Executor} implementation is used instead of {@link ScheduledExecutorService} then a system-wide Timer will be used to handle delayed events.
*/
public class ExecutorScheduler extends AbstractScheduler {
private final Executor executor;
/**
* Setup a ScheduledExecutorService that we can use if someone provides an Executor instead of ScheduledExecutorService.
*/
private final static ScheduledExecutorService SYSTEM_SCHEDULED_EXECUTOR;
static {
int count = Runtime.getRuntime().availableProcessors();
if (count > 8) {
count = count / 2;
}
// we don't need more than 8 to handle just scheduling and doing no work
if (count > 8) {
count = 8;
}
SYSTEM_SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(count, new ThreadFactory() {
final AtomicInteger counter = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "RxScheduledExecutorPool-" + counter.incrementAndGet());
t.setDaemon(true);
return t;
}
});
}
public ExecutorScheduler(Executor executor) {
this.executor = executor;
}
public ExecutorScheduler(ScheduledExecutorService executor) {
this.executor = executor;
}
@Override
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
final DiscardableAction discardableAction = new DiscardableAction(action);
if (executor instanceof ScheduledExecutorService) {
((ScheduledExecutorService) executor).schedule(new Runnable() {
@Override
public void run() {
discardableAction.call();
}
}, dueTime, unit);
} else {
if (dueTime == 0) {
// no delay so put on the thread-pool right now
return (schedule(action));
} else {
// there is a delay and this isn't a ScheduledExecutorService so we'll use a system-wide ScheduledExecutorService
// to handle the scheduling and once it's ready then execute on this Executor
SYSTEM_SCHEDULED_EXECUTOR.schedule(new Runnable() {
@Override
public void run() {
// now execute on the real Executor
executor.execute(new Runnable() {
@Override
public void run() {
discardableAction.call();
}
});
}
}, dueTime, unit);
}
}
return discardableAction;
}
@Override
public Subscription schedule(Func0<Subscription> action) {
final DiscardableAction discardableAction = new DiscardableAction(action);
executor.execute(new Runnable() {
@Override
public void run() {
discardableAction.call();
}
});
return discardableAction;
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.concurrent.TimeUnit;
import rx.Subscription;
import rx.util.functions.Func0;
/**
* Executes work immediately on the current thread.
*/
public final class ImmediateScheduler extends AbstractScheduler {
private static final ImmediateScheduler INSTANCE = new ImmediateScheduler();
public static ImmediateScheduler getInstance() {
return INSTANCE;
}
private ImmediateScheduler() {
}
@Override
public Subscription schedule(Func0<Subscription> action) {
return action.call();
}
@Override
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
return schedule(new SleepingAction(action, this, dueTime, unit));
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.concurrent.TimeUnit;
import rx.Subscription;
import rx.util.functions.Func0;
/**
* Schedules work on a new thread.
*/
public class NewThreadScheduler extends AbstractScheduler {
private static final NewThreadScheduler INSTANCE = new NewThreadScheduler();
public static NewThreadScheduler getInstance() {
return INSTANCE;
}
@Override
public Subscription schedule(Func0<Subscription> action) {
final DiscardableAction discardableAction = new DiscardableAction(action);
Thread t = new Thread(discardableAction::call, "RxNewThreadScheduler");
t.start();
return discardableAction;
}
@Override
public Subscription schedule(Func0<Subscription> action, long dueTime, TimeUnit unit) {
return schedule(new SleepingAction(action, this, dueTime, unit));
}
}

View File

@@ -0,0 +1,143 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import rx.Scheduler;
/**
* Static factory methods for creating Schedulers.
*/
public class Schedulers {
private static final ScheduledExecutorService COMPUTATION_EXECUTOR = createComputationExecutor();
private static final Executor IO_EXECUTOR = createIOExecutor();
private Schedulers() {
}
/**
* {@link Scheduler} that executes work immediately on the current thread.
*
* @return {@link ImmediateScheduler} instance
*/
public static Scheduler immediate() {
return ImmediateScheduler.getInstance();
}
/**
* {@link Scheduler} that queues work on the current thread to be executed after the current work completes.
*
* @return {@link CurrentThreadScheduler} instance
*/
public static Scheduler currentThread() {
return CurrentThreadScheduler.getInstance();
}
/**
* {@link Scheduler} that creates a new {@link Thread} for each unit of work.
*
* @return {@link NewThreadScheduler} instance
*/
public static Scheduler newThread() {
return NewThreadScheduler.getInstance();
}
/**
* {@link Scheduler} that queues work on an {@link Executor}.
* <p>
* Note that this does not support scheduled actions with a delay.
*
* @return {@link ExecutorScheduler} instance
*/
public static Scheduler executor(Executor executor) {
return new ExecutorScheduler(executor);
}
/**
* {@link Scheduler} that queues work on an {@link ScheduledExecutorService}.
*
* @return {@link ExecutorScheduler} instance
*/
public static Scheduler executor(ScheduledExecutorService executor) {
return new ExecutorScheduler(executor);
}
/**
* {@link Scheduler} intended for computational work.
* <p>
* The implementation is backed by a {@link ScheduledExecutorService} thread-pool sized to the number of CPU cores.
* <p>
* This can be used for event-loops, processing callbacks and other computational work.
* <p>
* Do not perform IO-bound work on this scheduler. Use {@link #threadPoolForComputation()} instead.
*
* @return {@link ExecutorScheduler} for computation-bound work.
*/
public static Scheduler threadPoolForComputation() {
return executor(COMPUTATION_EXECUTOR);
}
/**
* {@link Scheduler} intended for IO-bound work.
* <p>
* The implementation is backed by an {@link Executor} thread-pool that will grow as needed.
* <p>
* This can be used for asynchronously performing blocking IO.
* <p>
* Do not perform computational work on this scheduler. Use {@link #threadPoolForComputation()} instead.
*
* @return {@link ExecutorScheduler} for IO-bound work.
*/
public static Scheduler threadPoolForIO() {
return executor(IO_EXECUTOR);
}
private static ScheduledExecutorService createComputationExecutor() {
int cores = Runtime.getRuntime().availableProcessors();
return Executors.newScheduledThreadPool(cores, new ThreadFactory() {
final AtomicInteger counter = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "RxComputationThreadPool-" + counter.incrementAndGet());
t.setDaemon(true);
return t;
}
});
}
private static Executor createIOExecutor() {
Executor result = Executors.newCachedThreadPool(new ThreadFactory() {
final AtomicLong counter = new AtomicLong();
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "RxIOThreadPool-" + counter.incrementAndGet());
t.setDaemon(true);
return t;
}
});
return result;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.concurrency;
import java.util.concurrent.TimeUnit;
import rx.Scheduler;
import rx.Subscription;
import rx.util.functions.Func0;
/* package */class SleepingAction implements Func0<Subscription> {
private final Func0<Subscription> underlying;
private final Scheduler scheduler;
private final long execTime;
public SleepingAction(Func0<Subscription> underlying, Scheduler scheduler, long timespan, TimeUnit timeUnit) {
this.underlying = underlying;
this.scheduler = scheduler;
this.execTime = scheduler.now() + timeUnit.toMillis(timespan);
}
@Override
public Subscription call() {
if (execTime < scheduler.now()) {
try {
Thread.sleep(scheduler.now() - execTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
return underlying.call();
}
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Rx Schedulers
*/
package rx.concurrency;

View File

@@ -0,0 +1,43 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.observables;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
/**
* An {@link Observable} that has been grouped by a key whose value can be obtained using {@link #getKey()} <p>
*
* @see {@link Observable#groupBy(Observable, Func1)}
*
* @param <K>
* @param <T>
*/
public class GroupedObservable<K, T> extends Observable<T> {
private final K key;
public GroupedObservable(K key, Func1<Observer<T>, Subscription> onSubscribe) {
super(onSubscribe);
this.key = key;
}
public K getKey() {
return key;
}
}

View File

@@ -0,0 +1,61 @@
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1;
import java.util.concurrent.atomic.AtomicBoolean;
public class OperationAll {
public static <T> Func1<Observer<Boolean>, Subscription> all(Observable<T> sequence, Func1<T, Boolean> predicate) {
return new AllObservable<>(sequence, predicate);
}
private static class AllObservable<T> implements Func1<Observer<Boolean>, Subscription> {
private final Observable<T> sequence;
private final Func1<T, Boolean> predicate;
private final AtomicBoolean status = new AtomicBoolean(true);
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
private AllObservable(Observable<T> sequence, Func1<T, Boolean> predicate) {
this.sequence = sequence;
this.predicate = predicate;
}
@Override
public Subscription call(final Observer<Boolean> observer) {
return subscription.wrap(sequence.subscribe(new Observer<T>() {
@Override
public void onCompleted() {
if (status.get()) {
observer.onNext(true);
observer.onCompleted();
}
}
@Override
public void onError(Exception e) {
observer.onError(e);
}
@Override
public void onNext(T args) {
boolean result = predicate.call(args);
boolean changed = status.compareAndSet(true, result);
if (changed && !result) {
observer.onNext(false);
observer.onCompleted();
subscription.unsubscribe();
}
}
}));
}
}
}

View File

@@ -0,0 +1,273 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
import rx.util.functions.Func2;
import rx.util.functions.Func3;
import rx.util.functions.Func4;
import rx.util.functions.FuncN;
import rx.util.functions.Functions;
public class OperationCombineLatest {
/**
* Combines the two given observables, emitting an event containing an aggregation of the latest values of each of the source observables
* each time an event is received from one of the source observables, where the aggregation is defined by the given function.
* @param w0 The first source observable.
* @param w1 The second source observable.
* @param combineLatestFunction The aggregation function used to combine the source observable values.
* @return A function from an observer to a subscription. This can be used to create an observable from.
*/
public static <T0, T1, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Func2<T0, T1, R> combineLatestFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
a.addObserver(new CombineObserver<R, T0>(a, w0));
a.addObserver(new CombineObserver<R, T1>(a, w1));
return a;
}
/**
* @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction)
*/
public static <T0, T1, T2, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Func3<T0, T1, T2, R> combineLatestFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
a.addObserver(new CombineObserver<R, T0>(a, w0));
a.addObserver(new CombineObserver<R, T1>(a, w1));
a.addObserver(new CombineObserver<R, T2>(a, w2));
return a;
}
/**
* @see #combineLatest(Observable w0, Observable w1, Func2 combineLatestFunction)
*/
public static <T0, T1, T2, T3, R> Func1<Observer<R>, Subscription> combineLatest(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Observable<T3> w3, Func4<T0, T1, T2, T3, R> combineLatestFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(combineLatestFunction));
a.addObserver(new CombineObserver<R, T0>(a, w0));
a.addObserver(new CombineObserver<R, T1>(a, w1));
a.addObserver(new CombineObserver<R, T2>(a, w2));
a.addObserver(new CombineObserver<R, T3>(a, w3));
return a;
}
private static class CombineObserver<R, T> implements Observer<T> {
final Observable<T> w;
final Aggregator<R> a;
private Subscription subscription;
public CombineObserver(Aggregator<R> a, Observable<T> w) {
this.a = a;
this.w = w;
}
public synchronized void startWatching() {
if (subscription != null) {
throw new RuntimeException("This should only be called once.");
}
subscription = w.subscribe(this);
}
@Override
public void onCompleted() {
a.complete(this);
}
@Override
public void onError(Exception e) {
a.error(e);
}
@Override
public void onNext(T args) {
a.next(this, args);
}
}
/**
* Receive notifications from each of the observables we are reducing and execute the combineLatestFunction
* whenever we have received an event from one of the observables, as soon as each Observable has received
* at least one event.
*/
private static class Aggregator<R> implements Func1<Observer<R>, Subscription> {
private Observer<R> observer;
private final FuncN<R> combineLatestFunction;
private final AtomicBoolean running = new AtomicBoolean(true);
// used as an internal lock for handling the latest values and the completed state of each observer
private final Object lockObject = new Object();
/**
* Store when an observer completes.
* <p>
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above.
* */
private final Set<CombineObserver<R, ?>> completed = new HashSet<>();
/**
* The latest value from each observer
* <p>
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above.
* */
private final Map<CombineObserver<R, ?>, Object> latestValue = new HashMap<>();
/**
* Whether each observer has a latest value at all.
* <p>
* Note that access to this set MUST BE SYNCHRONIZED via 'lockObject' above.
* */
private final Set<CombineObserver<R, ?>> hasLatestValue = new HashSet<>();
/**
* Ordered list of observers to combine.
* No synchronization is necessary as these can not be added or changed asynchronously.
*/
private final List<CombineObserver<R, ?>> observers = new LinkedList<>();
public Aggregator(FuncN<R> combineLatestFunction) {
this.combineLatestFunction = combineLatestFunction;
}
/**
* Receive notification of a Observer starting (meaning we should require it for aggregation)
*
* @param w The observer to add.
*/
<T> void addObserver(CombineObserver<R, T> w) {
observers.add(w);
}
/**
* Receive notification of a Observer completing its iterations.
*
* @param w The observer that has completed.
*/
<T> void complete(CombineObserver<R, T> w) {
synchronized(lockObject) {
// store that this CombineLatestObserver is completed
completed.add(w);
// if all CombineObservers are completed, we mark the whole thing as completed
if (completed.size() == observers.size()) {
if (running.get()) {
// mark ourselves as done
observer.onCompleted();
// just to ensure we stop processing in case we receive more onNext/complete/error calls after this
running.set(false);
}
}
}
}
/**
* Receive error for a Observer. Throw the error up the chain and stop processing.
*/
void error(Exception e) {
observer.onError(e);
/* tell all observers to unsubscribe since we had an error */
stop();
}
/**
* Receive the next value from an observer.
* <p>
* If we have received values from all observers, trigger the combineLatest function, otherwise store the value and keep waiting.
*
* @param w
* @param arg
*/
<T> void next(CombineObserver<R, T> w, T arg) {
if (observer == null) {
throw new RuntimeException("This shouldn't be running if an Observer isn't registered");
}
/* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */
if (!running.get()) {
return;
}
// define here so the variable is out of the synchronized scope
Object[] argsToCombineLatest = new Object[observers.size()];
// we synchronize everything that touches latest values
synchronized (lockObject) {
// remember this as the latest value for this observer
latestValue.put(w, arg);
// remember that this observer now has a latest value set
hasLatestValue.add(w);
// if all observers in the 'observers' list have a value, invoke the combineLatestFunction
for (CombineObserver<R, ?> rw : observers) {
if (!hasLatestValue.contains(rw)) {
// we don't have a value yet for each observer to combine, so we don't have a combined value yet either
return;
}
}
// if we get to here this means all the queues have data
int i = 0;
for (CombineObserver<R, ?> _w : observers) {
argsToCombineLatest[i++] = latestValue.get(_w);
}
}
// if we did not return above from the synchronized block we can now invoke the combineLatestFunction with all of the args
// we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling
// this 'next' method while another thread finishes calling this combineLatestFunction
observer.onNext(combineLatestFunction.call(argsToCombineLatest));
}
@Override
public Subscription call(Observer<R> observer) {
if (this.observer != null) {
throw new IllegalStateException("Only one Observer can subscribe to this Observable.");
}
this.observer = observer;
/* start the observers */
for (CombineObserver<R, ?> rw : observers) {
rw.startWatching();
}
return new Subscription() {
@Override
public void unsubscribe() {
stop();
}
};
}
private void stop() {
/* tell ourselves to stop processing onNext events */
running.set(false);
/* propogate to all observers to unsubscribe */
for (CombineObserver<R, ?> rw : observers) {
if (rw.subscription != null) {
rw.subscription.unsubscribe();
}
}
}
}
}

View File

@@ -0,0 +1,126 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Action1;
import rx.util.functions.Func1;
public final class OperationConcat {
/**
* Combine the observable sequences from the list of Observables into one observable sequence without any transformation.
*
* @param sequences
* An observable sequence of elements to project.
* @return An observable sequence whose elements are the result of combining the output from the list of Observables.
*/
public static <T> Func1<Observer<T>, Subscription> concat(final Observable<T>... sequences) {
return new Func1<Observer<T>, Subscription>() {
@Override
public Subscription call(Observer<T> observer) {
return new Concat<T>(sequences).call(observer);
}
};
}
public static <T> Func1<Observer<T>, Subscription> concat(final List<Observable<T>> sequences) {
@SuppressWarnings("unchecked")
Observable<T>[] o = sequences.toArray((Observable<T>[]) Array.newInstance(Observable.class, sequences.size()));
return concat(o);
}
public static <T> Func1<Observer<T>, Subscription> concat(final Observable<Observable<T>> sequences) {
final List<Observable<T>> list = new ArrayList<Observable<T>>();
sequences.toList().subscribe(new Action1<List<Observable<T>>>() {
@Override
public void call(List<Observable<T>> t1) {
list.addAll(t1);
}
});
return concat(list);
}
private static class Concat<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T>[] sequences;
private int num = 0;
private int count = 0;
private Subscription s;
Concat(final Observable<T>... sequences) {
this.sequences = sequences;
this.num = sequences.length - 1;
}
private final AtomicObservableSubscription Subscription = new AtomicObservableSubscription();
private final Subscription actualSubscription = new Subscription() {
@Override
public void unsubscribe() {
if (null != s)
s.unsubscribe();
}
};
public Subscription call(Observer<T> observer) {
s = sequences[count].subscribe(new ConcatObserver(observer));
return Subscription.wrap(actualSubscription);
}
private class ConcatObserver implements Observer<T> {
private final Observer<T> observer;
ConcatObserver(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onCompleted() {
if (num == count)
observer.onCompleted();
else {
count++;
s = sequences[count].subscribe(this);
}
}
@Override
public void onError(Exception e) {
observer.onError(e);
}
@Override
public void onNext(T args) {
observer.onNext(args);
}
}
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func0;
import rx.util.functions.Func1;
public final class OperationDefer {
public static <T> Func1<Observer<T>, Subscription> defer(final Func0<Observable<T>> observableFactory) {
return observableFactory.call()::subscribe;
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Notification;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
/**
* Dematerializes the explicit notification values of an observable sequence as implicit notifications.
* See http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx for the Microsoft Rx equivalent.
*/
public final class OperationDematerialize {
/**
* Dematerializes the explicit notification values of an observable sequence as implicit notifications.
*
* @param sequence
* An observable sequence containing explicit notification values which have to be turned into implicit notifications.
* @return An observable sequence exhibiting the behavior corresponding to the source sequence's notification values.
* @see http://msdn.microsoft.com/en-us/library/hh229047(v=vs.103).aspx
*/
public static <T> Func1<Observer<T>, Subscription> dematerialize(final Observable<Notification<T>> sequence) {
return new DematerializeObservable<>(sequence);
}
private static class DematerializeObservable<T> implements Func1<Observer<T>, Subscription> {
private final Observable<Notification<T>> sequence;
public DematerializeObservable(Observable<Notification<T>> sequence) {
this.sequence = sequence;
}
@Override
public Subscription call(final Observer<T> observer) {
return sequence.subscribe(new Observer<Notification<T>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Exception e) {
}
@Override
public void onNext(Notification<T> value) {
switch (value.getKind()) {
case OnNext:
observer.onNext(value.getValue());
break;
case OnError:
observer.onError(value.getException());
break;
case OnCompleted:
observer.onCompleted();
break;
}
}
});
}
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1;
public final class OperationFilter<T> {
public static <T> Func1<Observer<T>, Subscription> filter(Observable<T> that, Func1<T, Boolean> predicate) {
return new Filter<>(that, predicate);
}
private static class Filter<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> that;
private final Func1<T, Boolean> predicate;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
public Filter(Observable<T> that, Func1<T, Boolean> predicate) {
this.that = that;
this.predicate = predicate;
}
@Override
public Subscription call(final Observer<T> observer) {
return subscription.wrap(that.subscribe(new Observer<T>() {
@Override
public void onNext(T value) {
try {
if (predicate.call(value)) {
observer.onNext(value);
}
} catch (Exception ex) {
observer.onError(ex);
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
subscription.unsubscribe();
}
}
@Override
public void onError(Exception ex) {
observer.onError(ex);
}
@Override
public void onCompleted() {
observer.onCompleted();
}
}));
}
}
}

View File

@@ -0,0 +1,91 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Action0;
import rx.util.functions.Func1;
public final class OperationFinally {
/**
* Call a given action when a sequence completes (with or without an
* exception). The returned observable is exactly as threadsafe as the
* source observable.
* <p/>
* Note that "finally" is a Java reserved word and cannot be an identifier,
* so we use "finallyDo".
*
* @param sequence An observable sequence of elements
* @param action An action to be taken when the sequence is complete or throws an exception
* @return An observable sequence with the same elements as the input.
* After the last element is consumed (and {@link Observer#onCompleted} has been called),
* or after an exception is thrown (and {@link Observer#onError} has been called),
* the given action will be called.
* @see <a href="http://msdn.microsoft.com/en-us/library/hh212133(v=vs.103).aspx">MSDN Observable.Finally method</a>
*/
public static <T> Func1<Observer<T>, Subscription> finallyDo(final Observable<T> sequence, final Action0 action) {
return new Finally<>(sequence, action)::call;
}
private static class Finally<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> sequence;
private final Action0 finalAction;
Finally(final Observable<T> sequence, Action0 finalAction) {
this.sequence = sequence;
this.finalAction = finalAction;
}
@Override
public Subscription call(Observer<T> observer) {
return sequence.subscribe(new FinallyObserver(observer));
}
private class FinallyObserver implements Observer<T> {
private final Observer<T> observer;
FinallyObserver(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onCompleted() {
try {
observer.onCompleted();
} finally {
finalAction.call();
}
}
@Override
public void onError(Exception e) {
try {
observer.onError(e);
} finally {
finalAction.call();
}
}
@Override
public void onNext(T args) {
observer.onNext(args);
}
}
}
}

View File

@@ -0,0 +1,124 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
public final class OperationMap {
/**
* Accepts a sequence and a transformation function. Returns a sequence that is the result of
* applying the transformation function to each item in the sequence.
*
* @param sequence
* the input sequence.
* @param func
* a function to apply to each item in the sequence.
* @param <T>
* the type of the input sequence.
* @param <R>
* the type of the output sequence.
* @return a sequence that is the result of applying the transformation function to each item in the input sequence.
*/
public static <T, R> Func1<Observer<R>, Subscription> map(Observable<T> sequence, Func1<T, R> func) {
return new MapObservable<>(sequence, func);
}
/**
* Accepts a sequence of observable sequences and a transformation function. Returns a flattened sequence that is the result of
* applying the transformation function to each item in the sequence of each observable sequence.
* <p>
* The closure should return an Observable which will then be merged.
*
* @param sequence
* the input sequence.
* @param func
* a function to apply to each item in the sequence.
* @param <T>
* the type of the input sequence.
* @param <R>
* the type of the output sequence.
* @return a sequence that is the result of applying the transformation function to each item in the input sequence.
*/
public static <T, R> Func1<Observer<R>, Subscription> mapMany(Observable<T> sequence, Func1<T, Observable<R>> func) {
return OperationMerge.merge(Observable.create(map(sequence, func)));
}
/**
* An observable sequence that is the result of applying a transformation to each item in an input sequence.
*
* @param <T>
* the type of the input sequence.
* @param <R>
* the type of the output sequence.
*/
private static class MapObservable<T, R> implements Func1<Observer<R>, Subscription> {
public MapObservable(Observable<T> sequence, Func1<T, R> func) {
this.sequence = sequence;
this.func = func;
}
private final Observable<T> sequence;
private final Func1<T, R> func;
@Override
public Subscription call(Observer<R> observer) {
return sequence.subscribe(new MapObserver<>(observer, func));
}
}
/**
* An observer that applies a transformation function to each item and forwards the result to an inner observer.
*
* @param <T>
* the type of the observer items.
* @param <R>
* the type of the inner observer items.
*/
private static class MapObserver<T, R> implements Observer<T> {
public MapObserver(Observer<R> observer, Func1<T, R> func) {
this.observer = observer;
this.func = func;
}
Observer<R> observer;
Func1<T, R> func;
@Override
public void onNext(T value) {
try {
observer.onNext(func.call(value));
} catch (Exception ex) {
observer.onError(ex);
}
}
@Override
public void onError(Exception ex) {
observer.onError(ex);
}
@Override
public void onCompleted() {
observer.onCompleted();
}
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Notification;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
/**
* Materializes the implicit notifications of an observable sequence as explicit notification values.
* <p>
* In other words, converts a sequence of OnNext, OnError and OnCompleted events into a sequence of ObservableNotifications containing the OnNext, OnError and OnCompleted values.
* <p>
* See http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx for the Microsoft Rx equivalent.
*/
public final class OperationMaterialize {
/**
* Materializes the implicit notifications of an observable sequence as explicit notification values.
*
* @param sequence
* An observable sequence of elements to project.
* @return An observable sequence whose elements are the result of materializing the notifications of the given sequence.
* @see http://msdn.microsoft.com/en-us/library/hh229453(v=VS.103).aspx
*/
public static <T> Func1<Observer<Notification<T>>, Subscription> materialize(final Observable<T> sequence) {
return new MaterializeObservable<>(sequence);
}
private static class MaterializeObservable<T> implements Func1<Observer<Notification<T>>, Subscription> {
private final Observable<T> sequence;
public MaterializeObservable(Observable<T> sequence) {
this.sequence = sequence;
}
@Override
public Subscription call(final Observer<Notification<T>> observer) {
return sequence.subscribe(new Observer<T>() {
@Override
public void onCompleted() {
observer.onNext(new Notification<>());
observer.onCompleted();
}
@Override
public void onError(Exception e) {
observer.onNext(new Notification<>(e));
observer.onCompleted();
}
@Override
public void onNext(T value) {
observer.onNext(new Notification<>(value));
}
});
}
}
}

View File

@@ -0,0 +1,255 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.SynchronizedObserver;
import rx.util.functions.Func1;
public final class OperationMerge {
/**
* Flattens the observable sequences from the list of Observables into one observable sequence without any transformation.
*
* @param source
* An observable sequence of elements to project.
* @return An observable sequence whose elements are the result of flattening the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx
*/
public static <T> Func1<Observer<T>, Subscription> merge(final Observable<Observable<T>> source) {
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
return new MergeObservable<T>(source)::call;
}
public static <T> Func1<Observer<T>, Subscription> merge(final Observable<T>... sequences) {
return merge(Arrays.asList(sequences));
}
public static <T> Func1<Observer<T>, Subscription> merge(final List<Observable<T>> sequences) {
return merge(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
private volatile boolean unsubscribed = false;
@Override
public Subscription call(Observer<Observable<T>> observer) {
for (Observable<T> o : sequences) {
if (!unsubscribed) {
observer.onNext(o);
} else {
// break out of the loop if we are unsubscribed
break;
}
}
if (!unsubscribed) {
observer.onCompleted();
}
return () -> {
unsubscribed = true;
};
}
}));
}
/**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
* <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads.
* <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
* <p>
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
*
* @param <T>
*/
private static final class MergeObservable<T> implements Func1<Observer<T>, Subscription> {
private final Observable<Observable<T>> sequences;
private final MergeSubscription ourSubscription = new MergeSubscription();
private final AtomicBoolean stopped = new AtomicBoolean(false);
private volatile boolean parentCompleted = false;
private final ConcurrentHashMap<ChildObserver, ChildObserver> childObservers = new ConcurrentHashMap<>();
private final ConcurrentHashMap<ChildObserver, Subscription> childSubscriptions = new ConcurrentHashMap<>();
private MergeObservable(Observable<Observable<T>> sequences) {
this.sequences = sequences;
}
@Override
public Subscription call(Observer<T> actualObserver) {
/**
* We must synchronize a merge because we subscribe to multiple sequences in parallel that will each be emitting.
* <p>
* The calls from each sequence must be serialized.
* <p>
* Bug report: https://github.com/Netflix/RxJava/issues/200
*/
AtomicObservableSubscription subscription = new AtomicObservableSubscription(ourSubscription);
SynchronizedObserver<T> synchronizedObserver = new SynchronizedObserver<>(actualObserver, subscription);
/**
* Subscribe to the parent Observable to get to the children Observables
*/
sequences.subscribe(new ParentObserver(synchronizedObserver));
/* return our subscription to allow unsubscribing */
return subscription;
}
/**
* Manage the internal subscription with a thread-safe means of stopping/unsubscribing so we don't unsubscribe twice.
* <p>
* Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions.
*/
private class MergeSubscription implements Subscription {
@Override
public void unsubscribe() {
stop();
}
public boolean stop() {
// try setting to false unless another thread beat us
boolean didSet = stopped.compareAndSet(false, true);
if (didSet) {
// this thread won the race to stop, so unsubscribe from the actualSubscription
for (Subscription _s : childSubscriptions.values()) {
_s.unsubscribe();
}
return true;
} else {
// another thread beat us
return false;
}
}
}
/**
* Subscribe to the top level Observable to receive the sequence of Observable<T> children.
*
* @param <T>
*/
private class ParentObserver implements Observer<Observable<T>> {
private final Observer<T> actualObserver;
public ParentObserver(Observer<T> actualObserver) {
this.actualObserver = actualObserver;
}
@Override
public void onCompleted() {
parentCompleted = true;
// this *can* occur before the children are done, so if it does we won't send onCompleted
// but will let the child worry about it
// if however this completes and there are no children processing, then we will send onCompleted
if (childObservers.isEmpty()) {
if (!stopped.get()) {
if (ourSubscription.stop()) {
actualObserver.onCompleted();
}
}
}
}
@Override
public void onError(Exception e) {
actualObserver.onError(e);
}
@Override
public void onNext(Observable<T> childObservable) {
if (stopped.get()) {
// we won't act on any further items
return;
}
if (childObservable == null) {
throw new IllegalArgumentException("Observable<T> can not be null.");
}
/**
* For each child Observable we receive we'll subscribe with a separate Observer
* that will each then forward their sequences to the actualObserver.
* <p>
* We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle.
*/
ChildObserver _w = new ChildObserver(actualObserver);
childObservers.put(_w, _w);
Subscription _subscription = childObservable.subscribe(_w);
// remember this Observer and the subscription from it
childSubscriptions.put(_w, _subscription);
}
}
/**
* Subscribe to each child Observable<T> and forward their sequence of data to the actualObserver
*
*/
private class ChildObserver implements Observer<T> {
private final Observer<T> actualObserver;
public ChildObserver(Observer<T> actualObserver) {
this.actualObserver = actualObserver;
}
@Override
public void onCompleted() {
// remove self from map of Observers
childObservers.remove(this);
// if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver
// if the parent is not complete that means there is another sequence (and child Observer) to come
if (!stopped.get()) {
if (childObservers.isEmpty() && parentCompleted) {
if (ourSubscription.stop()) {
// this thread 'won' the race to unsubscribe/stop so let's send onCompleted
actualObserver.onCompleted();
}
}
}
}
@Override
public void onError(Exception e) {
if (!stopped.get()) {
if (ourSubscription.stop()) {
// this thread 'won' the race to unsubscribe/stop so let's send the error
actualObserver.onError(e);
}
}
}
@Override
public void onNext(T args) {
// in case the Observable is poorly behaved and doesn't listen to the unsubscribe request
// we'll ignore anything that comes in after we've unsubscribed
if (!stopped.get()) {
actualObserver.onNext(args);
}
}
}
}
}

View File

@@ -0,0 +1,327 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.CompositeException;
import rx.util.functions.Func1;
/**
* Same functionality as OperationMerge except that onError events will be skipped so that all onNext calls are passed on until all sequences finish with onComplete or onError, and then the first
* onError received (if any) will be passed on.
* <p>
* This allows retrieving all successful onNext calls without being blocked by an onError early in a sequence.
* <p>
* NOTE: If this is used on an infinite stream it will never call onError and effectively will swallow errors.
*/
public final class OperationMergeDelayError {
/**
* Flattens the observable sequences from the list of Observables into one observable sequence without any transformation and delays any onError calls until after all sequences have called
* onError or onComplete so as to allow all successful
* onNext calls to be received.
*
* @param source
* An observable sequence of elements to project.
* @return An observable sequence whose elements are the result of flattening the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh229099(v=vs.103).aspx
*/
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final Observable<Observable<T>> sequences) {
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
return new Func1<Observer<T>, Subscription>() {
@Override
public Subscription call(Observer<T> observer) {
return new MergeDelayErrorObservable<T>(sequences).call(observer);
}
};
}
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final Observable<T>... sequences) {
return mergeDelayError(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
private volatile boolean unsubscribed = false;
@Override
public Subscription call(Observer<Observable<T>> observer) {
for (Observable<T> o : sequences) {
if (!unsubscribed) {
observer.onNext(o);
} else {
// break out of the loop if we are unsubscribed
break;
}
}
if (!unsubscribed) {
observer.onCompleted();
}
return new Subscription() {
@Override
public void unsubscribe() {
unsubscribed = true;
}
};
}
}));
}
public static <T> Func1<Observer<T>, Subscription> mergeDelayError(final List<Observable<T>> sequences) {
return mergeDelayError(Observable.create(new Func1<Observer<Observable<T>>, Subscription>() {
private volatile boolean unsubscribed = false;
@Override
public Subscription call(Observer<Observable<T>> observer) {
for (Observable<T> o : sequences) {
if (!unsubscribed) {
observer.onNext(o);
} else {
// break out of the loop if we are unsubscribed
break;
}
}
if (!unsubscribed) {
observer.onCompleted();
}
return new Subscription() {
@Override
public void unsubscribe() {
unsubscribed = true;
}
};
}
}));
}
/**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
* <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads.
* <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
* <p>
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
*
* @param <T>
*/
private static final class MergeDelayErrorObservable<T> implements Func1<Observer<T>, Subscription> {
private final Observable<Observable<T>> sequences;
private final MergeSubscription ourSubscription = new MergeSubscription();
private AtomicBoolean stopped = new AtomicBoolean(false);
private volatile boolean parentCompleted = false;
private final ConcurrentHashMap<ChildObserver, ChildObserver> childObservers = new ConcurrentHashMap<ChildObserver, ChildObserver>();
private final ConcurrentHashMap<ChildObserver, Subscription> childSubscriptions = new ConcurrentHashMap<ChildObserver, Subscription>();
// onErrors we received that will be delayed until everything is completed and then sent
private ConcurrentLinkedQueue<Exception> onErrorReceived = new ConcurrentLinkedQueue<Exception>();
private MergeDelayErrorObservable(Observable<Observable<T>> sequences) {
this.sequences = sequences;
}
public Subscription call(Observer<T> actualObserver) {
/**
* Subscribe to the parent Observable to get to the children Observables
*/
sequences.subscribe(new ParentObserver(actualObserver));
/* return our subscription to allow unsubscribing */
return ourSubscription;
}
/**
* Manage the internal subscription with a thread-safe means of stopping/unsubscribing so we don't unsubscribe twice.
* <p>
* Also has the stop() method returning a boolean so callers know if their thread "won" and should perform further actions.
*/
private class MergeSubscription implements Subscription {
@Override
public void unsubscribe() {
stop();
}
public boolean stop() {
// try setting to false unless another thread beat us
boolean didSet = stopped.compareAndSet(false, true);
if (didSet) {
// this thread won the race to stop, so unsubscribe from the actualSubscription
for (Subscription _s : childSubscriptions.values()) {
_s.unsubscribe();
}
return true;
} else {
// another thread beat us
return false;
}
}
}
/**
* Subscribe to the top level Observable to receive the sequence of Observable<T> children.
*
* @param <T>
*/
private class ParentObserver implements Observer<Observable<T>> {
private final Observer<T> actualObserver;
public ParentObserver(Observer<T> actualObserver) {
this.actualObserver = actualObserver;
}
@Override
public void onCompleted() {
parentCompleted = true;
// this *can* occur before the children are done, so if it does we won't send onCompleted
// but will let the child worry about it
// if however this completes and there are no children processing, then we will send onCompleted
if (childObservers.size() == 0) {
if (!stopped.get()) {
if (ourSubscription.stop()) {
if (onErrorReceived.size() == 1) {
// an onError was received from 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(onErrorReceived.peek());
} else if (onErrorReceived.size() > 1) {
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(new CompositeException(onErrorReceived));
} else {
// no delayed error so send onCompleted
actualObserver.onCompleted();
}
}
}
}
}
@Override
public void onError(Exception e) {
actualObserver.onError(e);
}
@Override
public void onNext(Observable<T> childObservable) {
if (stopped.get()) {
// we won't act on any further items
return;
}
if (childObservable == null) {
throw new IllegalArgumentException("Observable<T> can not be null.");
}
/**
* For each child Observable we receive we'll subscribe with a separate Observer
* that will each then forward their sequences to the actualObserver.
* <p>
* We use separate child Observers for each sequence to simplify the onComplete/onError handling so each sequence has its own lifecycle.
*/
ChildObserver _w = new ChildObserver(actualObserver);
childObservers.put(_w, _w);
Subscription _subscription = childObservable.subscribe(_w);
// remember this Observer and the subscription from it
childSubscriptions.put(_w, _subscription);
}
}
/**
* Subscribe to each child Observable<T> and forward their sequence of data to the actualObserver
*
*/
private class ChildObserver implements Observer<T> {
private final Observer<T> actualObserver;
private volatile boolean finished = false;
public ChildObserver(Observer<T> actualObserver) {
this.actualObserver = actualObserver;
}
@Override
public void onCompleted() {
// remove self from map of Observers
childObservers.remove(this);
// if there are now 0 Observers left, so if the parent is also completed we send the onComplete to the actualObserver
// if the parent is not complete that means there is another sequence (and child Observer) to come
if (!stopped.get()) {
finishObserver();
}
}
@Override
public void onError(Exception e) {
if (!stopped.get()) {
onErrorReceived.add(e);
// mark this ChildObserver as done
childObservers.remove(this);
// but do NOT forward to actualObserver as we want other ChildObservers to continue until completion
// and we'll delay the sending of onError until all others are done
// we mark finished==true as a safety to ensure that if further calls to onNext occur we ignore them
finished = true;
// check for whether the parent is completed and if so then perform the 'finishing' actions
finishObserver();
}
}
/**
* onComplete and onError when called need to check for the parent being complete and if so send the onCompleted or onError to the actualObserver.
* <p>
* This does NOT get invoked if synchronous execution occurs, but will when asynchronously executing.
* <p>
* TestCase testErrorDelayed4WithThreading specifically tests this use case.
*/
private void finishObserver() {
if (childObservers.size() == 0 && parentCompleted) {
if (ourSubscription.stop()) {
// this thread 'won' the race to unsubscribe/stop so let's send onError or onCompleted
if (onErrorReceived.size() == 1) {
// an onError was received from 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(onErrorReceived.peek());
} else if (onErrorReceived.size() > 1) {
// an onError was received from more than 1 ChildObserver so we now send it as a delayed error
actualObserver.onError(new CompositeException(onErrorReceived));
} else {
// no delayed error so send onCompleted
actualObserver.onCompleted();
}
}
}
}
@Override
public void onNext(T args) {
// in case the Observable is poorly behaved and doesn't listen to the unsubscribe request
// we'll ignore anything that comes in after we've unsubscribed or an onError has been received and delayed
if (!stopped.get() && !finished) {
actualObserver.onNext(args);
}
}
}
}
}

View File

@@ -0,0 +1,106 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import rx.Observable;
import rx.Observer;
import rx.util.Exceptions;
/**
* Samples the most recent value in an observable sequence.
*/
public final class OperationMostRecent {
public static <T> Iterable<T> mostRecent(final Observable<T> source, T initialValue) {
MostRecentObserver<T> mostRecentObserver = new MostRecentObserver<>(initialValue);
MostRecentIterator<T> nextIterator = new MostRecentIterator<>(mostRecentObserver);
source.subscribe(mostRecentObserver);
return () -> nextIterator;
}
private static class MostRecentIterator<T> implements Iterator<T> {
private final MostRecentObserver<T> observer;
private MostRecentIterator(MostRecentObserver<T> observer) {
this.observer = observer;
}
@Override
public boolean hasNext() {
return !observer.isCompleted();
}
@Override
public T next() {
if (observer.getException() != null) {
throw Exceptions.propagate(observer.getException());
}
return observer.getRecentValue();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read only iterator");
}
}
private static class MostRecentObserver<T> implements Observer<T> {
private final AtomicBoolean completed = new AtomicBoolean(false);
private final AtomicReference<T> value;
private final AtomicReference<Exception> exception = new AtomicReference<>();
private MostRecentObserver(T value) {
this.value = new AtomicReference<>(value);
}
@Override
public void onCompleted() {
completed.set(true);
}
@Override
public void onError(Exception e) {
exception.set(e);
}
@Override
public void onNext(T args) {
value.set(args);
}
public boolean isCompleted() {
return completed.get();
}
public Exception getException() {
return exception.get();
}
public T getRecentValue() {
return value.get();
}
}
}

View File

@@ -0,0 +1,148 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Notification;
import rx.Observable;
import rx.Observer;
import rx.util.Exceptions;
/**
* Samples the next value (blocking without buffering) from in an observable sequence.
*/
public final class OperationNext {
public static <T> Iterable<T> next(final Observable<T> items) {
NextObserver<T> nextObserver = new NextObserver<>();
final NextIterator<T> nextIterator = new NextIterator<>(nextObserver);
items.materialize().subscribe(nextObserver);
return () -> nextIterator;
}
private static class NextIterator<T> implements Iterator<T> {
private final NextObserver<T> observer;
private NextIterator(NextObserver<T> observer) {
this.observer = observer;
}
@Override
public boolean hasNext() {
return !observer.isCompleted(false);
}
@Override
public T next() {
if (observer.isCompleted(true)) {
throw new IllegalStateException("Observable is completed");
}
observer.await();
try {
return observer.takeNext();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw Exceptions.propagate(e);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read only iterator");
}
}
private static class NextObserver<T> implements Observer<Notification<T>> {
private final BlockingQueue<Notification<T>> buf = new ArrayBlockingQueue<>(1);
private final AtomicBoolean waiting = new AtomicBoolean(false);
@Override
public void onCompleted() {
// ignore
}
@Override
public void onError(Exception e) {
// ignore
}
@Override
public void onNext(Notification<T> args) {
if (waiting.getAndSet(false) || !args.isOnNext()) {
Notification<T> toOffer = args;
while (!buf.offer(toOffer)) {
Notification<T> concurrentItem = buf.poll();
// in case if we won race condition with onComplete/onError method
if (!concurrentItem.isOnNext()) {
toOffer = concurrentItem;
}
}
}
}
public void await() {
waiting.set(true);
}
public boolean isCompleted(boolean rethrowExceptionIfExists) {
Notification<T> lastItem = buf.peek();
if (lastItem == null) {
return false;
}
if (lastItem.isOnError()) {
if (rethrowExceptionIfExists) {
throw Exceptions.propagate(lastItem.getException());
} else {
return true;
}
}
return lastItem.isOnCompleted();
}
public T takeNext() throws InterruptedException {
Notification<T> next = buf.take();
if (next.isOnError()) {
throw Exceptions.propagate(next.getException());
}
if (next.isOnCompleted()) {
throw new IllegalStateException("Observable is completed");
}
return next.getValue();
}
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Scheduler;
import rx.Subscription;
import rx.util.functions.Func1;
public class OperationObserveOn {
public static <T> Func1<Observer<T>, Subscription> observeOn(Observable<T> source, Scheduler scheduler) {
return new ObserveOn<>(source, scheduler);
}
private static class ObserveOn<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> source;
private final Scheduler scheduler;
public ObserveOn(Observable<T> source, Scheduler scheduler) {
this.source = source;
this.scheduler = scheduler;
}
@Override
public Subscription call(final Observer<T> observer) {
return source.subscribe(new ScheduledObserver<>(observer, scheduler));
}
}
}

View File

@@ -0,0 +1,99 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.CompositeException;
import rx.util.functions.Func1;
public final class OperationOnErrorResumeNextViaFunction<T> {
public static <T> Func1<Observer<T>, Subscription> onErrorResumeNextViaFunction(Observable<T> originalSequence, Func1<Exception, Observable<T>> resumeFunction) {
return new OnErrorResumeNextViaFunction<>(originalSequence, resumeFunction);
}
private static class OnErrorResumeNextViaFunction<T> implements Func1<Observer<T>, Subscription> {
private final Func1<Exception, Observable<T>> resumeFunction;
private final Observable<T> originalSequence;
public OnErrorResumeNextViaFunction(Observable<T> originalSequence, Func1<Exception, Observable<T>> resumeFunction) {
this.resumeFunction = resumeFunction;
this.originalSequence = originalSequence;
}
@Override
public Subscription call(final Observer<T> observer) {
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(new AtomicObservableSubscription());
// subscribe to the original Observable and remember the subscription
subscriptionRef.get().wrap(new AtomicObservableSubscription(originalSequence.subscribe(new Observer<T>() {
@Override
public void onNext(T value) {
// forward the successful calls
observer.onNext(value);
}
/**
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence.
*/
@Override
public void onError(Exception ex) {
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
// check that we have not been unsubscribed before we can process the error
if (currentSubscription != null) {
try {
Observable<T> resumeSequence = resumeFunction.call(ex);
/* error occurred, so switch subscription to the 'resumeSequence' */
AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer));
/* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) {
// we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below
// so we want to immediately unsubscribe from the resumeSequence we just subscribed to
innerSubscription.unsubscribe();
}
} catch (Exception e) {
// the resume function failed so we need to call onError
// I am using CompositeException so that both exceptions can be seen
observer.onError(new CompositeException("OnErrorResume function failed", Arrays.asList(ex, e)));
}
}
}
@Override
public void onCompleted() {
// forward the successful calls
observer.onCompleted();
}
})));
return () -> {
// this will get either the original, or the resumeSequence one and unsubscribe on it
Subscription s = subscriptionRef.getAndSet(null);
if (s != null)
s.unsubscribe();
};
}
}
}

View File

@@ -0,0 +1,91 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.concurrent.atomic.AtomicReference;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1;
public final class OperationOnErrorResumeNextViaObservable<T> {
public static <T> Func1<Observer<T>, Subscription> onErrorResumeNextViaObservable(Observable<T> originalSequence, Observable<T> resumeSequence) {
return new OnErrorResumeNextViaObservable<>(originalSequence, resumeSequence);
}
private static class OnErrorResumeNextViaObservable<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> resumeSequence;
private final Observable<T> originalSequence;
public OnErrorResumeNextViaObservable(Observable<T> originalSequence, Observable<T> resumeSequence) {
this.resumeSequence = resumeSequence;
this.originalSequence = originalSequence;
}
public Subscription call(final Observer<T> observer) {
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(subscription);
// subscribe to the original Observable and remember the subscription
subscription.wrap(originalSequence.subscribe(new Observer<T>() {
@Override
public void onNext(T value) {
// forward the successful calls
observer.onNext(value);
}
/**
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence.
*/
@Override
public void onError(Exception ex) {
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
// check that we have not been unsubscribed before we can process the error
if (currentSubscription != null) {
/* error occurred, so switch subscription to the 'resumeSequence' */
AtomicObservableSubscription innerSubscription = new AtomicObservableSubscription(resumeSequence.subscribe(observer));
/* we changed the sequence, so also change the subscription to the one of the 'resumeSequence' instead */
if (!subscriptionRef.compareAndSet(currentSubscription, innerSubscription)) {
// we failed to set which means 'subscriptionRef' was set to NULL via the unsubscribe below
// so we want to immediately unsubscribe from the resumeSequence we just subscribed to
innerSubscription.unsubscribe();
}
}
}
@Override
public void onCompleted() {
// forward the successful calls
observer.onCompleted();
}
}));
return () -> {
// this will get either the original, or the resumeSequence one and unsubscribe on it
Subscription s = subscriptionRef.getAndSet(null);
if (s != null)
s.unsubscribe();
};
}
}
}

View File

@@ -0,0 +1,108 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.CompositeException;
import rx.util.functions.Func1;
/**
* When an onError occurs the resumeFunction will be executed and it's response passed to onNext instead of calling onError.
*/
public final class OperationOnErrorReturn<T> {
public static <T> Func1<Observer<T>, Subscription> onErrorReturn(Observable<T> originalSequence, Func1<Exception, T> resumeFunction) {
return new OnErrorReturn<>(originalSequence, resumeFunction);
}
private static class OnErrorReturn<T> implements Func1<Observer<T>, Subscription> {
private final Func1<Exception, T> resumeFunction;
private final Observable<T> originalSequence;
public OnErrorReturn(Observable<T> originalSequence, Func1<Exception, T> resumeFunction) {
this.resumeFunction = resumeFunction;
this.originalSequence = originalSequence;
}
@Override
public Subscription call(final Observer<T> observer) {
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
// AtomicReference since we'll be accessing/modifying this across threads so we can switch it if needed
final AtomicReference<AtomicObservableSubscription> subscriptionRef = new AtomicReference<>(subscription);
// subscribe to the original Observable and remember the subscription
subscription.wrap(originalSequence.subscribe(new Observer<T>() {
@Override
public void onNext(T value) {
// forward the successful calls
observer.onNext(value);
}
/**
* Instead of passing the onError forward, we intercept and "resume" with the resumeSequence.
*/
@Override
public void onError(Exception ex) {
/* remember what the current subscription is so we can determine if someone unsubscribes concurrently */
AtomicObservableSubscription currentSubscription = subscriptionRef.get();
// check that we have not been unsubscribed before we can process the error
if (currentSubscription != null) {
try {
/* error occurred, so execute the function, give it the exception and call onNext with the response */
onNext(resumeFunction.call(ex));
/*
* we are not handling an exception thrown from this function ... should we do something?
* error handling within an error handler is a weird one to determine what we should do
* right now I'm going to just let it throw whatever exceptions occur (such as NPE)
* but I'm considering calling the original Observer.onError to act as if this OnErrorReturn operator didn't happen
*/
/* we are now completed */
onCompleted();
/* unsubscribe since it blew up */
currentSubscription.unsubscribe();
} catch (Exception e) {
// the return function failed so we need to call onError
// I am using CompositeException so that both exceptions can be seen
observer.onError(new CompositeException("OnErrorReturn function failed", Arrays.asList(ex, e)));
}
}
}
@Override
public void onCompleted() {
// forward the successful calls
observer.onCompleted();
}
}));
return () -> {
// this will get either the original, or the resumeSequence one and unsubscribe on it
Subscription s = subscriptionRef.getAndSet(null);
if (s != null)
s.unsubscribe();
};
}
}
}

View File

@@ -0,0 +1,130 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1;
import rx.util.functions.Func2;
public final class OperationScan {
/**
* Applies an accumulator function over an observable sequence and returns each intermediate result with the specified source and accumulator.
*
* @param sequence
* An observable sequence of elements to project.
* @param initialValue
* The initial (seed) accumulator value.
* @param accumulator
* An accumulator function to be invoked on each element from the sequence.
*
* @return An observable sequence whose elements are the result of accumulating the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx
*/
public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, T initialValue, Func2<T, T, T> accumulator) {
return new Accumulator<>(sequence, initialValue, accumulator);
}
/**
* Applies an accumulator function over an observable sequence and returns each intermediate result with the specified source and accumulator.
*
* @param sequence
* An observable sequence of elements to project.
* @param accumulator
* An accumulator function to be invoked on each element from the sequence.
*
* @return An observable sequence whose elements are the result of accumulating the output from the list of Observables.
* @see http://msdn.microsoft.com/en-us/library/hh211665(v=vs.103).aspx
*/
public static <T> Func1<Observer<T>, Subscription> scan(Observable<T> sequence, Func2<T, T, T> accumulator) {
return new Accumulator<>(sequence, null, accumulator);
}
private static class Accumulator<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> sequence;
private final T initialValue;
private final Func2<T, T, T> accumlatorFunction;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
private Accumulator(Observable<T> sequence, T initialValue, Func2<T, T, T> accumulator) {
this.sequence = sequence;
this.initialValue = initialValue;
this.accumlatorFunction = accumulator;
}
@Override
public Subscription call(final Observer<T> observer) {
return subscription.wrap(sequence.subscribe(new Observer<T>() {
private T acc = initialValue;
private boolean hasSentInitialValue = false;
/**
* We must synchronize this because we can't allow
* multiple threads to execute the 'accumulatorFunction' at the same time because
* the accumulator code very often will be doing mutation of the 'acc' object such as a non-threadsafe HashMap
*
* Because it's synchronized it's using non-atomic variables since everything in this method is single-threaded
*/
@Override
public synchronized void onNext(T value) {
if (acc == null) {
// we assume that acc is not allowed to be returned from accumulatorValue
// so it's okay to check null as being the state we initialize on
acc = value;
// this is all we do for this first value if we didn't have an initialValue
return;
}
if (!hasSentInitialValue) {
hasSentInitialValue = true;
observer.onNext(acc);
}
try {
acc = accumlatorFunction.call(acc, value);
if (acc == null) {
onError(new IllegalArgumentException("Null is an unsupported return value for an accumulator."));
return;
}
observer.onNext(acc);
} catch (Exception ex) {
observer.onError(ex);
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
subscription.unsubscribe();
}
}
@Override
public void onError(Exception ex) {
observer.onError(ex);
}
// synchronized because we access 'hasSentInitialValue'
@Override
public synchronized void onCompleted() {
// if only one sequence value existed, we send it without any accumulation
if (!hasSentInitialValue) {
observer.onNext(acc);
}
observer.onCompleted();
}
}));
}
}
}

View File

@@ -0,0 +1,98 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.concurrent.atomic.AtomicInteger;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
/**
* Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values.
*/
public final class OperationSkip {
/**
* Skips a specified number of contiguous values from the start of a Observable sequence and then returns the remaining values.
*
* @param items
* @param num
* @return
*
* @see http://msdn.microsoft.com/en-us/library/hh229847(v=vs.103).aspx
*/
public static <T> Func1<Observer<T>, Subscription> skip(final Observable<T> items, final int num) {
// wrap in a Observable so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
return new Skip<>(items, num)::call;
}
/**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
* <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads.
*
* @param <T>
*/
private static class Skip<T> implements Func1<Observer<T>, Subscription> {
private final int num;
private final Observable<T> items;
Skip(final Observable<T> items, final int num) {
this.num = num;
this.items = items;
}
@Override
public Subscription call(Observer<T> observer) {
return items.subscribe(new ItemObserver(observer));
}
/**
* Used to subscribe to the 'items' Observable sequence and forward to the actualObserver up to 'num' count.
*/
private class ItemObserver implements Observer<T> {
private AtomicInteger counter = new AtomicInteger();
private final Observer<T> observer;
public ItemObserver(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onCompleted() {
observer.onCompleted();
}
@Override
public void onError(Exception e) {
observer.onError(e);
}
@Override
public void onNext(T args) {
// skip them until we reach the 'num' value
if (counter.incrementAndGet() > num) {
observer.onNext(args);
}
}
}
}
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Scheduler;
import rx.Subscription;
import rx.util.functions.Action0;
import rx.util.functions.Func1;
public class OperationSubscribeOn {
public static <T> Func1<Observer<T>, Subscription> subscribeOn(Observable<T> source, Scheduler scheduler) {
return new SubscribeOn<>(source, scheduler);
}
private static class SubscribeOn<T> implements Func1<Observer<T>, Subscription> {
private final Observable<T> source;
private final Scheduler scheduler;
public SubscribeOn(Observable<T> source, Scheduler scheduler) {
this.source = source;
this.scheduler = scheduler;
}
@Override
public Subscription call(final Observer<T> observer) {
return scheduler.schedule(() -> new ScheduledSubscription(source.subscribe(observer), scheduler));
}
}
private static class ScheduledSubscription implements Subscription {
private final Subscription underlying;
private final Scheduler scheduler;
private ScheduledSubscription(Subscription underlying, Scheduler scheduler) {
this.underlying = underlying;
this.scheduler = scheduler;
}
@Override
public void unsubscribe() {
scheduler.schedule(new Action0() {
@Override
public void call() {
underlying.unsubscribe();
}
});
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.SynchronizedObserver;
import rx.util.functions.Func1;
/**
* An observable that wraps an observable of the same type and then enforces the semantics
* expected of a well-behaved observable.
* <p>
* An observable that ensures onNext, onCompleted, or onError calls on its subscribers are
* not interleaved, onCompleted and onError are only called once respectively, and no
* onNext calls follow onCompleted and onError calls.
* <p>
* NOTE: {@link Observable#create} already wraps Observables so this is generally redundant.
*
* @param <T>
* The type of the observable sequence.
*/
public final class OperationSynchronize<T> {
/**
* Accepts an observable and wraps it in another observable which ensures that the resulting observable is well-behaved.
*
* A well-behaved observable ensures onNext, onCompleted, or onError calls to its subscribers are
* not interleaved, onCompleted and onError are only called once respectively, and no
* onNext calls follow onCompleted and onError calls.
*
* @param observable
* @param <T>
* @return
*/
public static <T> Func1<Observer<T>, Subscription> synchronize(Observable<T> observable) {
return new Synchronize<>(observable);
}
private static class Synchronize<T> implements Func1<Observer<T>, Subscription> {
public Synchronize(Observable<T> innerObservable) {
this.innerObservable = innerObservable;
}
private final Observable<T> innerObservable;
private SynchronizedObserver<T> atomicObserver;
@Override
public Subscription call(Observer<T> observer) {
AtomicObservableSubscription subscription = new AtomicObservableSubscription();
atomicObserver = new SynchronizedObserver<>(observer, subscription);
return subscription.wrap(innerObservable.subscribe(atomicObserver));
}
}
}

View File

@@ -0,0 +1,132 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.concurrent.atomic.AtomicInteger;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.subscriptions.Subscriptions;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1;
/**
* Returns a specified number of contiguous values from the start of an observable sequence.
*/
public final class OperationTake {
/**
* Returns a specified number of contiguous values from the start of an observable sequence.
*
* @param items
* @param num
* @return
*/
public static <T> Func1<Observer<T>, Subscription> take(final Observable<T> items, final int num) {
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
return new Take<>(items, num)::call;
}
/**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
* <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads.
* <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
* <p>
* Note how the take() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
*
* @param <T>
*/
private static class Take<T> implements Func1<Observer<T>, Subscription> {
private final AtomicInteger counter = new AtomicInteger();
private final Observable<T> items;
private final int num;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
private Take(Observable<T> items, int num) {
this.items = items;
this.num = num;
}
@Override
public Subscription call(Observer<T> observer) {
if (num < 1) {
items.subscribe(new Observer<T>()
{
@Override
public void onCompleted()
{
}
@Override
public void onError(Exception e)
{
}
@Override
public void onNext(T args)
{
}
}).unsubscribe();
observer.onCompleted();
return Subscriptions.empty();
}
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
}
private class ItemObserver implements Observer<T> {
private final Observer<T> observer;
public ItemObserver(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onCompleted() {
if (counter.getAndSet(num) < num) {
observer.onCompleted();
}
}
@Override
public void onError(Exception e) {
if (counter.getAndSet(num) < num) {
observer.onError(e);
}
}
@Override
public void onNext(T args) {
final int count = counter.incrementAndGet();
if (count <= num) {
observer.onNext(args);
if (count == num) {
observer.onCompleted();
}
}
if (count >= num) {
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
subscription.unsubscribe();
}
}
}
}
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingDeque;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.functions.Func1;
/**
* Returns a specified number of contiguous elements from the end of an observable sequence.
*/
public final class OperationTakeLast {
public static <T> Func1<Observer<T>, Subscription> takeLast(final Observable<T> items, final int count) {
return new TakeLast<>(items, count)::call;
}
private static class TakeLast<T> implements Func1<Observer<T>, Subscription> {
private final int count;
private final Observable<T> items;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
TakeLast(final Observable<T> items, final int count) {
this.count = count;
this.items = items;
}
@Override
public Subscription call(Observer<T> observer) {
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
}
private class ItemObserver implements Observer<T> {
private LinkedBlockingDeque<T> deque = new LinkedBlockingDeque<>(count);
private final Observer<T> observer;
public ItemObserver(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onCompleted() {
Iterator<T> reverse = deque.descendingIterator();
while (reverse.hasNext()) {
observer.onNext(reverse.next());
}
observer.onCompleted();
}
@Override
public void onError(Exception e) {
observer.onError(e);
}
@Override
public void onNext(T args) {
while (!deque.offerFirst(args)) {
deque.removeLast();
}
}
}
}
}

View File

@@ -0,0 +1,131 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.concurrent.atomic.AtomicInteger;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.AtomicObserver;
import rx.util.functions.Func1;
import rx.util.functions.Func2;
/**
* Returns values from an observable sequence as long as a specified condition is true, and then skips the remaining values.
*/
public final class OperationTakeWhile {
/**
* Returns a specified number of contiguous values from the start of an observable sequence.
*
* @param items
* @param predicate
* a function to test each source element for a condition
* @return
*/
public static <T> Func1<Observer<T>, Subscription> takeWhile(final Observable<T> items, final Func1<T, Boolean> predicate) {
return takeWhileWithIndex(items, OperationTakeWhile.<T> skipIndex(predicate));
}
/**
* Returns values from an observable sequence as long as a specified condition is true, and then skips the remaining values.
*
* @param items
* @param predicate
* a function to test each element for a condition; the second parameter of the function represents the index of the source element; otherwise, false.
* @return
*/
public static <T> Func1<Observer<T>, Subscription> takeWhileWithIndex(final Observable<T> items, final Func2<T, Integer, Boolean> predicate) {
// wrap in a Func so that if a chain is built up, then asynchronously subscribed to twice we will have 2 instances of Take<T> rather than 1 handing both, which is not thread-safe.
return new TakeWhile<T>(items, predicate)::call;
}
private static <T> Func2<T, Integer, Boolean> skipIndex(final Func1<T, Boolean> underlying) {
return (T input, Integer index) -> underlying.call(input);
}
/**
* This class is NOT thread-safe if invoked and referenced multiple times. In other words, don't subscribe to it multiple times from different threads.
* <p>
* It IS thread-safe from within it while receiving onNext events from multiple threads.
* <p>
* This should all be fine as long as it's kept as a private class and a new instance created from static factory method above.
* <p>
* Note how the takeWhileWithIndex() factory method above protects us from a single instance being exposed with the Observable wrapper handling the subscribe flow.
*
* @param <T>
*/
private static class TakeWhile<T> implements Func1<Observer<T>, Subscription> {
private final AtomicInteger counter = new AtomicInteger();
private final Observable<T> items;
private final Func2<T, Integer, Boolean> predicate;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
private TakeWhile(Observable<T> items, Func2<T, Integer, Boolean> predicate) {
this.items = items;
this.predicate = predicate;
}
@Override
public Subscription call(Observer<T> observer) {
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
}
private class ItemObserver implements Observer<T> {
private final Observer<T> observer;
public ItemObserver(Observer<T> observer) {
// Using AtomicObserver because the unsubscribe, onCompleted, onError and error handling behavior
// needs "isFinished" logic to not send duplicated events
// The 'testTakeWhile1' and 'testTakeWhile2' tests fail without this.
this.observer = new AtomicObserver<>(subscription, observer);
}
@Override
public void onCompleted() {
observer.onCompleted();
}
@Override
public void onError(Exception e) {
observer.onError(e);
}
@Override
public void onNext(T args) {
Boolean isSelected;
try {
isSelected = predicate.call(args, counter.getAndIncrement());
} catch (Exception e) {
observer.onError(e);
return;
}
if (isSelected) {
observer.onNext(args);
} else {
observer.onCompleted();
// this will work if the sequence is asynchronous, it will have no effect on a synchronous observable
subscription.unsubscribe();
}
}
}
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import rx.Observer;
import rx.Subscription;
import rx.subscriptions.Subscriptions;
import rx.util.functions.Func1;
public class OperationToObservableFuture {
private static class ToObservableFuture<T> implements Func1<Observer<T>, Subscription> {
private final Future<T> that;
private final Long time;
private final TimeUnit unit;
public ToObservableFuture(Future<T> that) {
this.that = that;
this.time = null;
this.unit = null;
}
public ToObservableFuture(Future<T> that, long time, TimeUnit unit) {
this.that = that;
this.time = time;
this.unit = unit;
}
@Override
public Subscription call(Observer<T> observer) {
try {
T value = (time == null) ? that.get() : that.get(time, unit);
if (!that.isCancelled()) {
observer.onNext(value);
}
observer.onCompleted();
} catch (Exception e) {
observer.onError(e);
}
// the get() has already completed so there is no point in
// giving the user a way to cancel.
return Subscriptions.empty();
}
}
public static <T> Func1<Observer<T>, Subscription> toObservableFuture(final Future<T> that) {
return new ToObservableFuture<>(that);
}
public static <T> Func1<Observer<T>, Subscription> toObservableFuture(final Future<T> that, long time, TimeUnit unit) {
return new ToObservableFuture<>(that, time, unit);
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observer;
import rx.Subscription;
import rx.subscriptions.Subscriptions;
import rx.util.functions.Func1;
/**
* Accepts an Iterable object and exposes it as an Observable.
*
* @param <T>
* The type of the Iterable sequence.
*/
public final class OperationToObservableIterable<T> {
public static <T> Func1<Observer<T>, Subscription> toObservableIterable(Iterable<T> iterable) {
return (Observer<T> observer) -> {
for (T item : iterable)
observer.onNext(item);
observer.onCompleted();
return Subscriptions.empty();
};
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
public final class OperationToObservableList<T> {
public static <T> Func1<Observer<List<T>>, Subscription> toObservableList(Observable<T> that) {
return new ToObservableList<>(that);
}
private static class ToObservableList<T> implements Func1<Observer<List<T>>, Subscription> {
private final Observable<T> that;
public ToObservableList(Observable<T> that) {
this.that = that;
}
@Override
public Subscription call(final Observer<List<T>> observer) {
return that.subscribe(new Observer<T>() {
final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>();
@Override
public void onNext(T value) {
// onNext can be concurrently executed so list must be thread-safe
list.add(value);
}
@Override
public void onError(Exception ex) {
observer.onError(ex);
}
@Override
public void onCompleted() {
try {
// copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface
ArrayList<T> l = new ArrayList<>(list.size());
for (T t : list)
l.add(t);
// benjchristensen => I want to make this list immutable but some clients are sorting this
// instead of using toSortedList() and this change breaks them until we migrate their code.
// observer.onNext(Collections.unmodifiableList(l));
observer.onNext(l);
observer.onCompleted();
} catch (Exception e) {
onError(e);
}
}
});
}
}
}

View File

@@ -0,0 +1,130 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
import rx.util.functions.Func2;
/**
* Similar to toList in that it converts a sequence<T> into a List<T> except that it accepts a Function that will provide an implementation of Comparator.
*
* @param <T>
*/
public final class OperationToObservableSortedList<T> {
/**
* Sort T objects by their natural order (object must implement Comparable).
*
* @param sequence
* @throws ClassCastException
* if T objects do not implement Comparable
* @return
*/
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence) {
return new ToObservableSortedList<>(sequence);
}
/**
* Sort T objects using the defined sort function.
*
* @param sequence
* @param sortFunction
* @return
*/
public static <T> Func1<Observer<List<T>>, Subscription> toSortedList(Observable<T> sequence, Func2<T, T, Integer> sortFunction) {
return new ToObservableSortedList<>(sequence, sortFunction);
}
private static class ToObservableSortedList<T> implements Func1<Observer<List<T>>, Subscription> {
private final Observable<T> that;
private final ConcurrentLinkedQueue<T> list = new ConcurrentLinkedQueue<>();
private final Func2<T, T, Integer> sortFunction;
// unchecked as we're support Object for the default
@SuppressWarnings("unchecked")
private ToObservableSortedList(Observable<T> that) {
this(that, defaultSortFunction);
}
private ToObservableSortedList(Observable<T> that, Func2<T, T, Integer> sortFunction) {
this.that = that;
this.sortFunction = sortFunction;
}
@Override
public Subscription call(final Observer<List<T>> observer) {
return that.subscribe(new Observer<T>() {
@Override
public void onNext(T value) {
// onNext can be concurrently executed so list must be thread-safe
list.add(value);
}
@Override
public void onError(Exception ex) {
observer.onError(ex);
}
@Override
public void onCompleted() {
try {
// copy from LinkedQueue to List since ConcurrentLinkedQueue does not implement the List interface
ArrayList<T> l = new ArrayList<>(list.size());
for (T t : list) {
l.add(t);
}
// sort the list before delivery
Collections.sort(l, (T o1, T o2) -> sortFunction.call(o1, o2));
observer.onNext(Collections.unmodifiableList(l));
observer.onCompleted();
} catch (Exception e) {
onError(e);
}
}
});
}
// raw because we want to support Object for this default
@SuppressWarnings("rawtypes")
private static Func2 defaultSortFunction = new DefaultComparableFunction();
private static class DefaultComparableFunction implements Func2<Object, Object, Integer> {
// unchecked because we want to support Object for this default
@SuppressWarnings("unchecked")
@Override
public Integer call(Object t1, Object t2) {
Comparable<Object> c1 = (Comparable<Object>) t1;
Comparable<Object> c2 = (Comparable<Object>) t2;
return c1.compareTo(c2);
}
}
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
public final class OperationWhere {
public static <T> Func1<Observer<T>, Subscription> where(Observable<T> that, Func1<T, Boolean> predicate) {
return OperationFilter.filter(that, predicate);
}
}

View File

@@ -0,0 +1,259 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.SynchronizedObserver;
import rx.util.functions.Func1;
import rx.util.functions.Func2;
import rx.util.functions.Func3;
import rx.util.functions.Func4;
import rx.util.functions.FuncN;
import rx.util.functions.Functions;
public final class OperationZip {
public static <T0, T1, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Func2<T0, T1, R> zipFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
a.addObserver(new ZipObserver<>(a, w0));
a.addObserver(new ZipObserver<>(a, w1));
return a;
}
public static <T0, T1, T2, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Func3<T0, T1, T2, R> zipFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
a.addObserver(new ZipObserver<>(a, w0));
a.addObserver(new ZipObserver<>(a, w1));
a.addObserver(new ZipObserver<>(a, w2));
return a;
}
public static <T0, T1, T2, T3, R> Func1<Observer<R>, Subscription> zip(Observable<T0> w0, Observable<T1> w1, Observable<T2> w2, Observable<T3> w3, Func4<T0, T1, T2, T3, R> zipFunction) {
Aggregator<R> a = new Aggregator<>(Functions.fromFunc(zipFunction));
a.addObserver(new ZipObserver<>(a, w0));
a.addObserver(new ZipObserver<>(a, w1));
a.addObserver(new ZipObserver<>(a, w2));
a.addObserver(new ZipObserver<>(a, w3));
return a;
}
/*
* ThreadSafe
*/
private static class ZipObserver<R, T> implements Observer<T> {
final Observable<T> w;
final Aggregator<R> a;
private final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
private final AtomicBoolean subscribed = new AtomicBoolean(false);
public ZipObserver(Aggregator<R> a, Observable<T> w) {
this.a = a;
this.w = w;
}
public void startWatching() {
if (subscribed.compareAndSet(false, true)) {
// only subscribe once even if called more than once
subscription.wrap(w.subscribe(this));
}
}
@Override
public void onCompleted() {
a.complete(this);
}
@Override
public void onError(Exception e) {
a.error(this, e);
}
@Override
public void onNext(T args) {
try {
a.next(this, args);
} catch (Exception e) {
onError(e);
}
}
}
/**
* Receive notifications from each of the Observables we are reducing and execute the zipFunction whenever we have received events from all Observables.
*
* This class is thread-safe.
*
* @param <T>
*/
private static class Aggregator<T> implements Func1<Observer<T>, Subscription> {
private volatile SynchronizedObserver<T> observer;
private final FuncN<T> zipFunction;
private final AtomicBoolean started = new AtomicBoolean(false);
private final AtomicBoolean running = new AtomicBoolean(true);
private final ConcurrentHashMap<ZipObserver<T, ?>, Boolean> completed = new ConcurrentHashMap<>();
/* we use ConcurrentHashMap despite synchronization of methods because stop() does NOT use synchronization and this map is used by it and can be called by other threads */
private final ConcurrentHashMap<ZipObserver<T, ?>, ConcurrentLinkedQueue<Object>> receivedValuesPerObserver = new ConcurrentHashMap<>();
/* we use a ConcurrentLinkedQueue to retain ordering (I'd like to just use a ConcurrentLinkedHashMap for 'receivedValuesPerObserver' but that doesn't exist in standard java */
private final ConcurrentLinkedQueue<ZipObserver<T, ?>> observers = new ConcurrentLinkedQueue<>();
public Aggregator(FuncN<T> zipFunction) {
this.zipFunction = zipFunction;
}
/**
* Receive notification of a Observer starting (meaning we should require it for aggregation)
*
* Thread Safety => Invoke ONLY from the static factory methods at top of this class which are always an atomic execution by a single thread.
*
* @param w
*/
private void addObserver(ZipObserver<T, ?> w) {
// initialize this ZipObserver
observers.add(w);
receivedValuesPerObserver.put(w, new ConcurrentLinkedQueue<>());
}
/**
* Receive notification of a Observer completing its iterations.
*
* @param w
*/
void complete(ZipObserver<T, ?> w) {
// store that this ZipObserver is completed
completed.put(w, Boolean.TRUE);
// if all ZipObservers are completed, we mark the whole thing as completed
if (completed.size() == observers.size()) {
if (running.compareAndSet(true, false)) {
// this thread succeeded in setting running=false so let's propagate the completion
// mark ourselves as done
observer.onCompleted();
}
}
}
/**
* Receive error for a Observer. Throw the error up the chain and stop processing.
*
* @param w
*/
void error(ZipObserver<T, ?> w, Exception e) {
if (running.compareAndSet(true, false)) {
// this thread succeeded in setting running=false so let's propagate the error
observer.onError(e);
/* since we receive an error we want to tell everyone to stop */
stop();
}
}
/**
* Receive the next value from a Observer.
* <p>
* If we have received values from all Observers, trigger the zip function, otherwise store the value and keep waiting.
*
* @param w
* @param arg
*/
void next(ZipObserver<T, ?> w, Object arg) {
if (observer == null) {
throw new RuntimeException("This shouldn't be running if a Observer isn't registered");
}
/* if we've been 'unsubscribed' don't process anything further even if the things we're watching keep sending (likely because they are not responding to the unsubscribe call) */
if (!running.get()) {
return;
}
// store the value we received and below we'll decide if we are to send it to the Observer
receivedValuesPerObserver.get(w).add(arg);
// define here so the variable is out of the synchronized scope
Object[] argsToZip = new Object[observers.size()];
/* we have to synchronize here despite using concurrent data structures because the compound logic here must all be done atomically */
synchronized (this) {
// if all ZipObservers in 'receivedValues' map have a value, invoke the zipFunction
for (ZipObserver<T, ?> rw : receivedValuesPerObserver.keySet()) {
if (receivedValuesPerObserver.get(rw).peek() == null) {
// we have a null meaning the queues aren't all populated so won't do anything
return;
}
}
// if we get to here this means all the queues have data
int i = 0;
for (ZipObserver<T, ?> rw : observers) {
argsToZip[i++] = receivedValuesPerObserver.get(rw).remove();
}
}
// if we did not return above from the synchronized block we can now invoke the zipFunction with all of the args
// we do this outside the synchronized block as it is now safe to call this concurrently and don't need to block other threads from calling
// this 'next' method while another thread finishes calling this zipFunction
observer.onNext(zipFunction.call(argsToZip));
}
@Override
public Subscription call(Observer<T> observer) {
if (started.compareAndSet(false, true)) {
AtomicObservableSubscription subscription = new AtomicObservableSubscription();
this.observer = new SynchronizedObserver<>(observer, subscription);
/* start the Observers */
for (ZipObserver<T, ?> rw : observers) {
rw.startWatching();
}
return subscription.wrap(this::stop);
} else {
/* a Observer already has subscribed so blow up */
throw new IllegalStateException("Only one Observer can subscribe to this Observable.");
}
}
/*
* Do NOT synchronize this because it gets called via unsubscribe which can occur on other threads
* and result in deadlocks. (http://jira/browse/API-4060)
*
* AtomicObservableSubscription uses compareAndSet instead of locking to avoid deadlocks but ensure single-execution.
*
* We do the same in the implementation of this method.
*
* ThreadSafety of this method is provided by:
* - AtomicBoolean[running].compareAndSet
* - ConcurrentLinkedQueue[Observers]
* - ZipObserver.subscription being an AtomicObservableSubscription
*/
private void stop() {
/* tell ourselves to stop processing onNext events by setting running=false */
if (running.compareAndSet(true, false)) {
/* propogate to all Observers to unsubscribe if this thread succeeded in setting running=false */
for (ZipObserver<T, ?> o : observers) {
if (o.subscription != null) {
o.subscription.unsubscribe();
}
}
}
}
}
}

View File

@@ -0,0 +1,116 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import java.util.concurrent.ConcurrentHashMap;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.observables.GroupedObservable;
import rx.util.functions.Func1;
import rx.util.functions.Functions;
public final class OperatorGroupBy {
public static <K, T, R> Func1<Observer<GroupedObservable<K, R>>, Subscription> groupBy(Observable<T> source, final Func1<T, K> keySelector, final Func1<T, R> elementSelector) {
final Observable<KeyValue<K, R>> keyval = source.map(new Func1<T, KeyValue<K, R>>() {
@Override
public KeyValue<K, R> call(T t) {
K key = keySelector.call(t);
R value = elementSelector.call(t);
return new KeyValue<K, R>(key, value);
}
});
return new GroupBy<K, R>(keyval);
}
public static <K, T> Func1<Observer<GroupedObservable<K, T>>, Subscription> groupBy(Observable<T> source, final Func1<T, K> keySelector) {
return groupBy(source, keySelector, Functions.<T> identity());
}
private static class GroupBy<K, V> implements Func1<Observer<GroupedObservable<K, V>>, Subscription> {
private final Observable<KeyValue<K, V>> source;
private final ConcurrentHashMap<K, Boolean> keys = new ConcurrentHashMap<K, Boolean>();
private GroupBy(Observable<KeyValue<K, V>> source) {
this.source = source;
}
@Override
public Subscription call(final Observer<GroupedObservable<K, V>> observer) {
return source.subscribe(new Observer<KeyValue<K, V>>() {
@Override
public void onCompleted() {
observer.onCompleted();
}
@Override
public void onError(Exception e) {
observer.onError(e);
}
@Override
public void onNext(final KeyValue<K, V> args) {
K key = args.key;
boolean newGroup = keys.putIfAbsent(key, true) == null;
if (newGroup) {
observer.onNext(buildObservableFor(source, key));
}
}
});
}
}
private static <K, R> GroupedObservable<K, R> buildObservableFor(Observable<KeyValue<K, R>> source, final K key) {
final Observable<R> observable = source.filter(new Func1<KeyValue<K, R>, Boolean>() {
@Override
public Boolean call(KeyValue<K, R> pair) {
return key.equals(pair.key);
}
}).map(new Func1<KeyValue<K, R>, R>() {
@Override
public R call(KeyValue<K, R> pair) {
return pair.value;
}
});
return new GroupedObservable<K, R>(key, new Func1<Observer<R>, Subscription>() {
@Override
public Subscription call(Observer<R> observer) {
return observable.subscribe(observer);
}
});
}
private static class KeyValue<K, V> {
private final K key;
private final V value;
private KeyValue(K key, V value) {
this.key = key;
this.value = value;
}
}
}

View File

@@ -0,0 +1,137 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
public class OperatorTakeUntil {
/**
* Returns the values from the source observable sequence until the other observable sequence produces a value.
*
* @param source
* the source sequence to propagate elements for.
* @param other
* the observable sequence that terminates propagation of elements of the source sequence.
* @param <T>
* the type of source.
* @param <E>
* the other type.
* @return An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation.
*/
public static <T, E> Observable<T> takeUntil(final Observable<T> source, final Observable<E> other) {
Observable<Notification<T>> s = Observable.create(new SourceObservable<T>(source));
Observable<Notification<T>> o = Observable.create(new OtherObservable<T, E>(other));
@SuppressWarnings("unchecked")
/**
* In JDK 7 we could use 'varargs' instead of 'unchecked'.
* See http://stackoverflow.com/questions/1445233/is-it-possible-to-solve-the-a-generic-array-of-t-is-created-for-a-varargs-param
* and http://hg.openjdk.java.net/jdk7/tl/langtools/rev/46cf751559ae
*/
Observable<Notification<T>> result = Observable.merge(s, o);
return result.takeWhile(new Func1<Notification<T>, Boolean>() {
@Override
public Boolean call(Notification<T> notification) {
return !notification.halt;
}
}).map(new Func1<Notification<T>, T>() {
@Override
public T call(Notification<T> notification) {
return notification.value;
}
});
}
private static class Notification<T> {
private final boolean halt;
private final T value;
public static <T> Notification<T> value(T value) {
return new Notification<T>(false, value);
}
public static <T> Notification<T> halt() {
return new Notification<T>(true, null);
}
private Notification(boolean halt, T value) {
this.halt = halt;
this.value = value;
}
}
private static class SourceObservable<T> implements Func1<Observer<Notification<T>>, Subscription> {
private final Observable<T> sequence;
private SourceObservable(Observable<T> sequence) {
this.sequence = sequence;
}
@Override
public Subscription call(final Observer<Notification<T>> notificationObserver) {
return sequence.subscribe(new Observer<T>() {
@Override
public void onCompleted() {
notificationObserver.onNext(Notification.<T> halt());
}
@Override
public void onError(Exception e) {
notificationObserver.onError(e);
}
@Override
public void onNext(T args) {
notificationObserver.onNext(Notification.value(args));
}
});
}
}
private static class OtherObservable<T, E> implements Func1<Observer<Notification<T>>, Subscription> {
private final Observable<E> sequence;
private OtherObservable(Observable<E> sequence) {
this.sequence = sequence;
}
@Override
public Subscription call(final Observer<Notification<T>> notificationObserver) {
return sequence.subscribe(new Observer<E>() {
@Override
public void onCompleted() {
// Ignore
}
@Override
public void onError(Exception e) {
notificationObserver.onError(e);
}
@Override
public void onNext(E args) {
notificationObserver.onNext(Notification.<T> halt());
}
});
}
}
}

View File

@@ -0,0 +1,85 @@
package rx.operators;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import rx.Notification;
import rx.Observable;
import rx.Observer;
import rx.util.Exceptions;
/**
* @see https://github.com/Netflix/RxJava/issues/50
*/
public class OperatorToIterator {
/**
* Returns an iterator that iterates all values of the observable.
*
* @param that
* an observable sequence to get an iterator for.
* @param <T>
* the type of source.
* @return the iterator that could be used to iterate over the elements of the observable.
*/
public static <T> Iterator<T> toIterator(Observable<T> that) {
final BlockingQueue<Notification<T>> notifications = new LinkedBlockingQueue<Notification<T>>();
Observable.materialize(that).subscribe(new Observer<Notification<T>>() {
@Override
public void onCompleted() {
// ignore
}
@Override
public void onError(Exception e) {
// ignore
}
@Override
public void onNext(Notification<T> args) {
notifications.offer(args);
}
});
return new Iterator<T>() {
private Notification<T> buf;
@Override
public boolean hasNext() {
if (buf == null) {
buf = take();
}
return !buf.isOnCompleted();
}
@Override
public T next() {
if (buf == null) {
buf = take();
}
if (buf.isOnError()) {
throw Exceptions.propagate(buf.getException());
}
T result = buf.getValue();
buf = null;
return result;
}
private Notification<T> take() {
try {
return notifications.take();
} catch (InterruptedException e) {
throw Exceptions.propagate(e);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read-only iterator");
}
};
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.operators;
import rx.Observer;
import rx.Scheduler;
import rx.util.functions.Action0;
/* package */class ScheduledObserver<T> implements Observer<T> {
private final Observer<T> underlying;
private final Scheduler scheduler;
public ScheduledObserver(Observer<T> underlying, Scheduler scheduler) {
this.underlying = underlying;
this.scheduler = scheduler;
}
@Override
public void onCompleted() {
scheduler.schedule(new Action0() {
@Override
public void call() {
underlying.onCompleted();
}
});
}
@Override
public void onError(final Exception e) {
scheduler.schedule(new Action0() {
@Override
public void call() {
underlying.onError(e);
}
});
}
@Override
public void onNext(final T args) {
scheduler.schedule(new Action0() {
@Override
public void call() {
underlying.onNext(args);
}
});
}
}

View File

@@ -0,0 +1,23 @@
<!--
Copyright 2013 Netflix, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<body>
<p>Operators that allow composing Observables to transform and
manipulate data in an asynchronous, functional and thread-safe manner.</p>
<p>The operators are all exposed via the ObservableExtensions class</p>
</body>

View File

@@ -0,0 +1,45 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>Rx Observables</p>
*
* <p>A library that enables subscribing to and composing asynchronous events and
* callbacks.</p>
* <p>The Observable/Observer interfaces and associated operators (in
* the .operations package) are inspired by and attempt to conform to the
* Reactive Rx library in Microsoft .Net.</p>
* <p>
* More information can be found at <a
* href="http://msdn.microsoft.com/en-us/data/gg577609">http://msdn.microsoft.com/en-us/data/gg577609</a>.
* </p>
*
*
* <p>Compared with the Microsoft implementation:
* <ul>
* <li>Observable == IObservable</li>
* <li>Observer == IObserver</li>
* <li>Subscription == IDisposable</li>
* <li>ObservableExtensions == Observable</li>
* </ul>
* </p>
* <p>Services which intend on exposing data asynchronously and wish
* to allow reactive processing and composition can implement the
* Watchable interface which then allows Watchers to subscribe to them
* and receive events.</p>
* <p>Usage examples can be found on the Watchable and Watcher
* classes.</p>
*/
package rx;

View File

@@ -0,0 +1,41 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.plugins;
import rx.Observable;
import rx.Observer;
/**
* Abstract class for defining error handling logic in addition to the normal {@link Observer#onError(Exception)} behavior.
* <p>
* For example, all Exceptions can be logged using this handler even if {@link Observer#onError(Exception)} is ignored or not provided when an {@link Observable} is subscribed to.
* <p>
* See {@link RxJavaPlugins} or the RxJava GitHub Wiki for information on configuring plugins: <a
* href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
*/
public abstract class RxJavaErrorHandler {
/**
* Receives all Exceptions from an {@link Observable} passed to {@link Observer#onError(Exception)}.
*
* @param e
* Exception
*/
public void handleError(Exception e) {
// do nothing by default
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.plugins;
/**
* Default implementation of {@link RxJavaErrorHandler} that does nothing.
*
* @ExcludeFromJavadoc
*/
public class RxJavaErrorHandlerDefault extends RxJavaErrorHandler {
private static RxJavaErrorHandlerDefault INSTANCE = new RxJavaErrorHandlerDefault();
public static RxJavaErrorHandler getInstance() {
return INSTANCE;
}
}

View File

@@ -0,0 +1,88 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.plugins;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.functions.Func1;
/**
* Abstract ExecutionHook with invocations at different lifecycle points of {@link Observable} execution with a default no-op implementation.
* <p>
* See {@link RxJavaPlugins} or the RxJava GitHub Wiki for information on configuring plugins: <a
* href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
* <p>
* <b>Note on thread-safety and performance</b>
* <p>
* A single implementation of this class will be used globally so methods on this class will be invoked concurrently from multiple threads so all functionality must be thread-safe.
* <p>
* Methods are also invoked synchronously and will add to execution time of the observable so all behavior should be fast. If anything time-consuming is to be done it should be spawned asynchronously
* onto separate worker threads.
*
* */
public abstract class RxJavaObservableExecutionHook {
/**
* Invoked before {@link Observable#subscribe(rx.Observer)} is about to be executed.
* <p>
* This can be used to decorate or replace the <code>onSubscribe</code> function or just perform extra logging, metrics and other such things and pass-thru the function.
*
* @param observableInstance
* The executing {@link Observable} instance.
* @param onSubscribe
* original {@link Func1}<{@link Observer}{@code<T>}, {@link Subscription}> to be executed
* @return {@link Func1}<{@link Observer}{@code<T>}, {@link Subscription}> function that can be modified, decorated, replaced or just returned as a pass-thru.
*/
public <T> Func1<Observer<T>, Subscription> onSubscribeStart(Observable<T> observableInstance, Func1<Observer<T>, Subscription> onSubscribe) {
// pass-thru by default
return onSubscribe;
}
/**
* Invoked after successful execution of {@link Observable#subscribe(rx.Observer)} with returned {@link Subscription}.
* <p>
* This can be used to decorate or replace the {@link Subscription} instance or just perform extra logging, metrics and other such things and pass-thru the subscription.
*
* @param observableInstance
* The executing {@link Observable} instance.
* @param subscription
* original {@link Subscription}
* @return {@link Subscription} subscription that can be modified, decorated, replaced or just returned as a pass-thru.
*/
public <T> Subscription onSubscribeReturn(Observable<T> observableInstance, Subscription subscription) {
// pass-thru by default
return subscription;
}
/**
* Invoked after failed execution of {@link Observable#subscribe(Observer)} with thrown Exception.
* <p>
* This is NOT errors emitted via {@link Observer#onError(Exception)} but exceptions thrown when attempting
* to subscribe to a {@link Func1}<{@link Observer}{@code<T>}, {@link Subscription}>.
*
* @param observableInstance
* The executing {@link Observable} instance.
* @param e
* Exception thrown by {@link Observable#subscribe(Observer)}
* @return Exception that can be decorated, replaced or just returned as a pass-thru.
*/
public <T> Exception onSubscribeError(Observable<T> observableInstance, Exception e) {
// pass-thru by default
return e;
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.plugins;
/**
* Default no-op implementation of {@link RxJavaObservableExecutionHook}
*/
/* package */class RxJavaObservableExecutionHookDefault extends RxJavaObservableExecutionHook {
private static RxJavaObservableExecutionHookDefault INSTANCE = new RxJavaObservableExecutionHookDefault();
public static RxJavaObservableExecutionHook getInstance() {
return INSTANCE;
}
}

View File

@@ -0,0 +1,147 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.plugins;
import java.util.concurrent.atomic.AtomicReference;
/**
* Registry for plugin implementations that allows global override and handles the retrieval of correct implementation based on order of precedence:
* <ol>
* <li>plugin registered globally via <code>register</code> methods in this class</li>
* <li>plugin registered and retrieved using {@link java.lang.System#getProperty(String)} (see get methods for property names)</li>
* <li>default implementation</li>
* </ol>
* See the RxJava GitHub Wiki for more information: <a href="https://github.com/Netflix/RxJava/wiki/Plugins">https://github.com/Netflix/RxJava/wiki/Plugins</a>.
*/
public class RxJavaPlugins {
private final static RxJavaPlugins INSTANCE = new RxJavaPlugins();
private final AtomicReference<RxJavaErrorHandler> errorHandler = new AtomicReference<RxJavaErrorHandler>();
private final AtomicReference<RxJavaObservableExecutionHook> observableExecutionHook = new AtomicReference<RxJavaObservableExecutionHook>();
private RxJavaPlugins() {
}
public static RxJavaPlugins getInstance() {
return INSTANCE;
}
/**
* Retrieve instance of {@link RxJavaErrorHandler} to use based on order of precedence as defined in {@link RxJavaPlugins} class header.
* <p>
* Override default by using {@link #registerErrorHandler(RxJavaErrorHandler)} or setting property: <code>rxjava.plugin.RxJavaErrorHandler.implementation</code> 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);
if (impl == null) {
// nothing set via properties so initialize with default
errorHandler.compareAndSet(null, RxJavaErrorHandlerDefault.getInstance());
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
} else {
// we received an implementation from the system property so use it
errorHandler.compareAndSet(null, (RxJavaErrorHandler) impl);
}
}
return errorHandler.get();
}
/**
* Register a {@link RxJavaErrorHandler} implementation as a global override of any injected or default implementations.
*
* @param impl
* {@link RxJavaErrorHandler} implementation
* @throws IllegalStateException
* if called more than once or after the default was initialized (if usage occurs before trying to register)
*/
public void registerErrorHandler(RxJavaErrorHandler impl) {
if (!errorHandler.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered.");
}
}
/**
* Retrieve instance of {@link RxJavaObservableExecutionHook} to use based on order of precedence as defined in {@link RxJavaPlugins} class header.
* <p>
* Override default by using {@link #registerObservableExecutionHook(RxJavaObservableExecutionHook)} or setting property: <code>rxjava.plugin.RxJavaObservableExecutionHook.implementation</code>
* with the full classname to load.
*
* @return {@link RxJavaObservableExecutionHook} implementation to use
*/
public RxJavaObservableExecutionHook getObservableExecutionHook() {
if (observableExecutionHook.get() == null) {
// check for an implementation from System.getProperty first
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class);
if (impl == null) {
// nothing set via properties so initialize with default
observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance());
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
} else {
// we received an implementation from the system property so use it
observableExecutionHook.compareAndSet(null, (RxJavaObservableExecutionHook) impl);
}
}
return observableExecutionHook.get();
}
/**
* Register a {@link RxJavaObservableExecutionHook} implementation as a global override of any injected or default implementations.
*
* @param impl
* {@link RxJavaObservableExecutionHook} implementation
* @throws IllegalStateException
* if called more than once or after the default was initialized (if usage occurs before trying to register)
*/
public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl) {
if (!observableExecutionHook.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered.");
}
}
private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
String classSimpleName = pluginClass.getSimpleName();
/*
* Check system properties for plugin class.
* <p>
* 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");
if (implementingClass != null) {
try {
Class<?> cls = Class.forName(implementingClass);
// narrow the scope (cast) to the type we're expecting
cls = cls.asSubclass(pluginClass);
return cls.newInstance();
} catch (ClassCastException e) {
throw new RuntimeException(classSimpleName + " implementation is not an instance of " + classSimpleName + ": " + implementingClass);
} catch (ClassNotFoundException e) {
throw new RuntimeException(classSimpleName + " implementation class not found: " + implementingClass, e);
} catch (InstantiationException e) {
throw new RuntimeException(classSimpleName + " implementation not able to be instantiated: " + implementingClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e);
}
} else {
return null;
}
}
}

View File

@@ -0,0 +1,65 @@
package rx.subjects;
import java.util.concurrent.ConcurrentHashMap;
import rx.Observable;
import rx.Observer;
import rx.Subscription;
import rx.util.AtomicObservableSubscription;
import rx.util.SynchronizedObserver;
import rx.util.functions.Func1;
public class Subject<T> extends Observable<T> implements Observer<T> {
public static <T> Subject<T> create() {
final ConcurrentHashMap<Subscription, Observer<T>> observers = new ConcurrentHashMap<Subscription, Observer<T>>();
Func1<Observer<T>, Subscription> onSubscribe = new Func1<Observer<T>, Subscription>() {
@Override
public Subscription call(Observer<T> observer) {
final AtomicObservableSubscription subscription = new AtomicObservableSubscription();
subscription.wrap(new Subscription() {
@Override
public void unsubscribe() {
// on unsubscribe remove it from the map of outbound observers to notify
observers.remove(subscription);
}
});
// on subscribe add it to the map of outbound observers to notify
observers.put(subscription, new SynchronizedObserver<T>(observer, subscription));
return subscription;
}
};
return new Subject<T>(onSubscribe, observers);
}
private final ConcurrentHashMap<Subscription, Observer<T>> observers;
protected Subject(Func1<Observer<T>, Subscription> onSubscribe, ConcurrentHashMap<Subscription, Observer<T>> observers) {
super(onSubscribe);
this.observers = observers;
}
@Override
public void onCompleted() {
for (Observer<T> observer : observers.values()) {
observer.onCompleted();
}
}
@Override
public void onError(Exception e) {
for (Observer<T> observer : observers.values()) {
observer.onError(e);
}
}
@Override
public void onNext(T args) {
for (Observer<T> observer : observers.values()) {
observer.onNext(args);
}
}
}

View File

@@ -0,0 +1,26 @@
package rx.subscriptions;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Observable;
import rx.Subscription;
/**
* Subscription that can be checked for status such as in a loop inside an {@link Observable} to exit the loop if unsubscribed.
*
* @see Rx.Net equivalent BooleanDisposable at http://msdn.microsoft.com/en-us/library/system.reactive.disposables.booleandisposable(v=vs.103).aspx
*/
public class BooleanSubscription implements Subscription {
private final AtomicBoolean unsubscribed = new AtomicBoolean(false);
public boolean isUnsubscribed() {
return unsubscribed.get();
}
@Override
public void unsubscribe() {
unsubscribed.set(true);
}
}

View File

@@ -0,0 +1,58 @@
package rx.subscriptions;
import rx.Subscription;
import rx.util.functions.Action0;
import rx.util.functions.FuncN;
import rx.util.functions.Functions;
public class Subscriptions {
/**
* A {@link Subscription} that does nothing.
*
* @return {@link Subscription}
*/
public static Subscription empty() {
return EMPTY;
}
/**
* A {@link Subscription} implemented via a Func
*
* @return {@link Subscription}
*/
public static Subscription create(final Action0 unsubscribe) {
return new Subscription() {
@Override
public void unsubscribe() {
unsubscribe.call();
}
};
}
/**
* A {@link Subscription} implemented via an anonymous function (such as closures from other languages).
*
* @return {@link Subscription}
*/
public static Subscription create(final Object unsubscribe) {
final FuncN<?> f = Functions.from(unsubscribe);
return new Subscription() {
@Override
public void unsubscribe() {
f.call();
}
};
}
/**
* A {@link Subscription} that does nothing when its unsubscribe method is called.
*/
private static Subscription EMPTY = new Subscription() {
public void unsubscribe() {
}
};
}

View File

@@ -0,0 +1,74 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import rx.Subscription;
/**
* Thread-safe wrapper around Observable Subscription that ensures unsubscribe can be called only once.
* <p>
* Also used to:
* <p>
* <ul>
* <li>allow the AtomicObserver to have access to the subscription in asynchronous execution for checking if unsubscribed occurred without onComplete/onError.</li>
* <li>handle both synchronous and asynchronous subscribe() execution flows</li>
* </ul>
*/
public final class AtomicObservableSubscription implements Subscription {
private AtomicReference<Subscription> actualSubscription = new AtomicReference<Subscription>();
private AtomicBoolean unsubscribed = new AtomicBoolean(false);
public AtomicObservableSubscription() {
}
public AtomicObservableSubscription(Subscription actualSubscription) {
this.actualSubscription.set(actualSubscription);
}
/**
* Wraps the actual subscription once it exists (if it wasn't available when constructed)
*
* @param actualSubscription
* @throws IllegalStateException
* if trying to set more than once (or use this method after setting via constructor)
*/
public AtomicObservableSubscription wrap(Subscription actualSubscription) {
if (!this.actualSubscription.compareAndSet(null, actualSubscription)) {
throw new IllegalStateException("Can not set subscription more than once.");
}
return this;
}
@Override
public void unsubscribe() {
// get the real thing and set to null in an atomic operation so we will only ever call unsubscribe once
Subscription actual = actualSubscription.getAndSet(null);
// if it's not null we will unsubscribe
if (actual != null) {
actual.unsubscribe();
unsubscribed.set(true);
}
}
public boolean isUnsubscribed() {
return unsubscribed.get();
}
}

View File

@@ -0,0 +1,97 @@
package rx.util;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import rx.Observer;
import rx.plugins.RxJavaPlugins;
/**
* Wrapper around Observer to ensure compliance with Rx contract.
* <p>
* The following is taken from the Rx Design Guidelines document: http://go.microsoft.com/fwlink/?LinkID=205219
* <pre>
* Messages sent to instances of the IObserver interface follow the following grammar:
*
* OnNext* (OnCompleted | OnError)?
*
* This grammar allows observable sequences to send any amount (0 or more) of OnNext messages to the subscribed
* observer instance, optionally followed by a single success (OnCompleted) or failure (OnError) message.
*
* The single message indicating that an observable sequence has finished ensures that consumers of the observable
* sequence can deterministically establish that it is safe to perform cleanup operations.
*
* A single failure further ensures that abort semantics can be maintained for operators that work on
* multiple observable sequences (see paragraph 6.6).
* </pre>
*
* <p>
* This wrapper will do the following:
* <ul>
* <li>Allow only single execution of either onError or onCompleted.</li>
* <li>Once an onComplete or onError are performed, no further calls can be executed</li>
* <li>If unsubscribe is called, this means we call completed() and don't allow any further onNext calls.</li>
* <li>When onError or onComplete occur it will unsubscribe from the Observable (if executing asynchronously).</li>
* </ul>
* <p>
* It will not synchronize onNext execution. Use the {@link SynchronizedObserver} to do that.
*
* @param <T>
*/
public class AtomicObserver<T> implements Observer<T> {
private final Observer<T> actual;
private final AtomicBoolean isFinished = new AtomicBoolean(false);
private final AtomicObservableSubscription subscription;
public AtomicObserver(AtomicObservableSubscription subscription, Observer<T> actual) {
this.subscription = subscription;
this.actual = actual;
}
@Override
public void onCompleted() {
if (isFinished.compareAndSet(false, true)) {
try {
actual.onCompleted();
} catch (Exception e) {
// handle errors if the onCompleted implementation fails, not just if the Observable fails
onError(e);
}
// auto-unsubscribe
subscription.unsubscribe();
}
}
@Override
public void onError(Exception e) {
if (isFinished.compareAndSet(false, true)) {
try {
actual.onError(e);
} catch (Exception e2) {
// if the onError itself fails then pass to the plugin
// see https://github.com/Netflix/RxJava/issues/216 for further discussion
RxJavaPlugins.getInstance().getErrorHandler().handleError(e);
RxJavaPlugins.getInstance().getErrorHandler().handleError(e2);
// and throw exception despite that not being proper for Rx
// https://github.com/Netflix/RxJava/issues/198
throw new RuntimeException("Error occurred when trying to propagate error to Observer.onError", new CompositeException(Arrays.asList(e, e2)));
}
// auto-unsubscribe
subscription.unsubscribe();
}
}
@Override
public void onNext(T args) {
try {
if (!isFinished.get()) {
actual.onNext(args);
}
} catch (Exception e) {
// handle errors if the onNext implementation fails, not just if the Observable fails
onError(e);
}
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Exception that is a composite of 1 or more other exceptions.
* <p>
* The <code>getMessage()</code> will return a concatenation of the composite exceptions.
*/
public class CompositeException extends RuntimeException {
private static final long serialVersionUID = 3026362227162912146L;
private final List<Exception> exceptions;
private final String message;
public CompositeException(String messagePrefix, Collection<Exception> errors) {
StringBuilder _message = new StringBuilder();
if (messagePrefix != null) {
_message.append(messagePrefix).append(" => ");
}
List<Exception> _exceptions = new ArrayList<Exception>();
for (Exception e : errors) {
_exceptions.add(e);
if (_message.length() > 0) {
_message.append(", ");
}
_message.append(e.getClass().getSimpleName()).append(":").append(e.getMessage());
}
this.exceptions = Collections.unmodifiableList(_exceptions);
this.message = _message.toString();
}
public CompositeException(Collection<Exception> errors) {
this(null, errors);
}
public List<Exception> getExceptions() {
return exceptions;
}
@Override
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util;
public class Exceptions {
private Exceptions() {
}
public static RuntimeException propagate(Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException(t);
}
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util;
import java.util.Iterator;
import java.util.NoSuchElementException;
public final class Range implements Iterable<Integer> {
private final int start;
private final int end;
private final int step;
public static Range createWithCount(int start, int count) {
return create(start, start + count);
}
public static Range create(int start, int end) {
return new Range(start, end, 1);
}
public static Range createWithStep(int start, int end, int step) {
return new Range(start, end, step);
}
private Range(int start, int end, int step) {
this.start = start;
this.end = end;
this.step = step;
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int current = start;
@Override
public boolean hasNext() {
return current < end;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException("No more elements");
}
int result = current;
current += step;
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read only iterator");
}
};
}
@Override
public String toString() {
return "Range (" + start + ", " + end + "), step " + step;
}
}

View File

@@ -0,0 +1,108 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util;
import rx.Observer;
/**
* A thread-safe Observer for transitioning states in operators.
* <p>
* Execution rules are:
* <ul>
* <li>Allow only single-threaded, synchronous, ordered execution of onNext, onCompleted, onError</li>
* <li>Once an onComplete or onError are performed, no further calls can be executed</li>
* <li>If unsubscribe is called, this means we call completed() and don't allow any further onNext calls.</li>
* </ul>
*
* @param <T>
*/
public final class SynchronizedObserver<T> implements Observer<T> {
/**
* Intrinsic synchronized locking with double-check short-circuiting was chosen after testing several other implementations.
*
* The code and results can be found here:
* - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/results/Observer
* - https://github.com/benjchristensen/JavaLockPerformanceTests/tree/master/src/com/benjchristensen/performance/locks/Observer
*
* The major characteristic that made me choose synchronized instead of Reentrant or a customer AbstractQueueSynchronizer implementation
* is that intrinsic locking performed better when nested, and AtomicObserver will end up nested most of the time since Rx is
* compositional by its very nature.
*
* // TODO composing of this class should rarely happen now with updated design so this decision should be revisited
*/
private final Observer<T> observer;
private final AtomicObservableSubscription subscription;
private volatile boolean finishRequested = false;
private volatile boolean finished = false;
public SynchronizedObserver(Observer<T> Observer, AtomicObservableSubscription subscription) {
this.observer = Observer;
this.subscription = subscription;
}
@Override
public void onNext(T arg) {
if (finished || finishRequested || subscription.isUnsubscribed()) {
// if we're already stopped, or a finish request has been received, we won't allow further onNext requests
return;
}
synchronized (this) {
// check again since this could have changed while waiting
if (finished || finishRequested || subscription.isUnsubscribed()) {
// if we're already stopped, or a finish request has been received, we won't allow further onNext requests
return;
}
observer.onNext(arg);
}
}
@Override
public void onError(Exception e) {
if (finished || subscription.isUnsubscribed()) {
// another thread has already finished us, so we won't proceed
return;
}
finishRequested = true;
synchronized (this) {
// check again since this could have changed while waiting
if (finished || subscription.isUnsubscribed()) {
return;
}
observer.onError(e);
finished = true;
}
}
@Override
public void onCompleted() {
if (finished || subscription.isUnsubscribed()) {
// another thread has already finished us, so we won't proceed
return;
}
finishRequested = true;
synchronized (this) {
// check again since this could have changed while waiting
if (finished || subscription.isUnsubscribed()) {
return;
}
observer.onCompleted();
finished = true;
}
}
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Action0 extends Function {
public void call();
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Action1<T1> extends Function {
public void call(T1 t1);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Action2<T1, T2> extends Function {
public void call(T1 t1, T2 t2);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Action3<T1, T2, T3> extends Function {
public void call(T1 t1, T2 t2, T3 t3);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func0<R> extends Function {
public R call();
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func1<T1, R> extends Function {
public R call(T1 t1);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func2<T1, T2, R> extends Function {
public R call(T1 t1, T2 t2);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func3<T1, T2, T3, R> extends Function {
public R call(T1 t1, T2 t2, T3 t3);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func4<T1, T2, T3, T4, R> extends Function {
public R call(T1 t1, T2 t2, T3 t3, T4 t4);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func5<T1, T2, T3, T4, T5, R> extends Function {
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func6<T1, T2, T3, T4, T5, T6, R> extends Function {
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func7<T1, T2, T3, T4, T5, T6, T7, R> extends Function {
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func8<T1, T2, T3, T4, T5, T6, T7, T8, R> extends Function {
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface Func9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> extends Function {
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9);
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface FuncN<R> extends Function {
public R call(Object... args);
}

View File

@@ -0,0 +1,10 @@
package rx.util.functions;
/**
* All Func and Action interfaces extend from this.
* <p>
* Marker interface to allow isntanceof checks.
*/
public interface Function {
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
public interface FunctionLanguageAdaptor {
/**
* Invoke the function and return the results.
*
* @param function
* @param args
* @return Object results from function execution
*/
Object call(Object function, Object[] args);
/**
* The Class of the Function that this adaptor serves.
* <p>
* Example: groovy.lang.Closure
* <p>
* This should not return classes of java.* packages.
*
* @return Class[] of classes that this adaptor should be invoked for.
*/
public Class<?>[] getFunctionClass();
}

View File

@@ -0,0 +1,579 @@
/**
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.util.functions;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import org.jackhuang.hellominecraft.logging.logger.Logger;
/**
* Allows execution of functions from multiple different languages.
* <p>
* Language support is provided via implementations of {@link FunctionLanguageAdaptor}.
* <p>
* This class will dynamically look for known language adaptors on the classpath at startup or new ones can be registered using {@link #registerLanguageAdaptor(Class[], FunctionLanguageAdaptor)}.
*/
public class Functions {
private static final Logger logger = new Logger("Functions");
private final static ConcurrentHashMap<Class<?>, FunctionLanguageAdaptor> languageAdaptors = new ConcurrentHashMap<Class<?>, FunctionLanguageAdaptor>();
static {
/* optimistically look for supported languages if they are in the classpath */
loadLanguageAdaptor("Groovy");
loadLanguageAdaptor("JRuby");
loadLanguageAdaptor("Clojure");
loadLanguageAdaptor("Scala");
// as new languages arise we can add them here but this does not prevent someone from using 'registerLanguageAdaptor' directly
}
private static boolean loadLanguageAdaptor(String name) {
String className = "rx.lang." + name.toLowerCase() + "." + name + "Adaptor";
try {
Class<?> c = Class.forName(className);
FunctionLanguageAdaptor a = (FunctionLanguageAdaptor) c.newInstance();
registerLanguageAdaptor(a.getFunctionClass(), a);
logger.info("Successfully loaded function language adaptor: " + name + " with path: " + className);
} catch (ClassNotFoundException e) {
logger.info("Could not find function language adaptor: " + name + " with path: " + className);
return false;
} catch (Exception e) {
logger.error("Failed trying to initialize function language adaptor: " + className, e);
return false;
}
return true;
}
public static void registerLanguageAdaptor(Class<?>[] functionClasses, FunctionLanguageAdaptor adaptor) {
for (Class<?> functionClass : functionClasses) {
if (functionClass.getPackage().getName().startsWith("java.")) {
throw new IllegalArgumentException("FunctionLanguageAdaptor implementations can not specify java.lang.* classes.");
}
languageAdaptors.put(functionClass, adaptor);
}
}
public static void removeLanguageAdaptor(Class<?> functionClass) {
languageAdaptors.remove(functionClass);
}
public static Collection<FunctionLanguageAdaptor> getRegisteredLanguageAdaptors() {
return languageAdaptors.values();
}
/**
* Utility method for determining the type of closure/function and executing it.
*
* @param function
*/
@SuppressWarnings({ "rawtypes" })
public static FuncN from(final Object function) {
if (function == null) {
throw new RuntimeException("function is null. Can't send arguments to null function.");
}
/* check for typed Rx Function implementation first */
if (function instanceof Function) {
return fromFunction((Function) function);
} else {
/* not an Rx Function so try language adaptors */
// check for language adaptor
for (final Class c : languageAdaptors.keySet()) {
if (c.isInstance(function)) {
final FunctionLanguageAdaptor la = languageAdaptors.get(c);
// found the language adaptor so wrap in FuncN and return
return new FuncN() {
@Override
public Object call(Object... args) {
return la.call(function, args);
}
};
}
}
// no language adaptor found
}
// no support found
throw new RuntimeException("Unsupported closure type: " + function.getClass().getSimpleName());
}
//
// @SuppressWarnings("unchecked")
// private static <R> R executionRxFunction(Function function, Object... args) {
// // check Func* classes
// if (function instanceof Func0) {
// Func0<R> f = (Func0<R>) function;
// if (args.length != 0) {
// throw new RuntimeException("The closure was Func0 and expected no arguments, but we received: " + args.length);
// }
// return (R) f.call();
// } else if (function instanceof Func1) {
// Func1<Object, R> f = (Func1<Object, R>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Func1 and expected 1 argument, but we received: " + args.length);
// }
// return f.call(args[0]);
// } else if (function instanceof Func2) {
// Func2<Object, Object, R> f = (Func2<Object, Object, R>) function;
// if (args.length != 2) {
// throw new RuntimeException("The closure was Func2 and expected 2 arguments, but we received: " + args.length);
// }
// return f.call(args[0], args[1]);
// } else if (function instanceof Func3) {
// Func3<Object, Object, Object, R> f = (Func3<Object, Object, Object, R>) function;
// if (args.length != 3) {
// throw new RuntimeException("The closure was Func3 and expected 3 arguments, but we received: " + args.length);
// }
// return (R) f.call(args[0], args[1], args[2]);
// } else if (function instanceof Func4) {
// Func4<Object, Object, Object, Object, R> f = (Func4<Object, Object, Object, Object, R>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Func4 and expected 4 arguments, but we received: " + args.length);
// }
// return f.call(args[0], args[1], args[2], args[3]);
// } else if (function instanceof Func5) {
// Func5<Object, Object, Object, Object, Object, R> f = (Func5<Object, Object, Object, Object, Object, R>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Func5 and expected 5 arguments, but we received: " + args.length);
// }
// return f.call(args[0], args[1], args[2], args[3], args[4]);
// } else if (function instanceof Func6) {
// Func6<Object, Object, Object, Object, Object, Object, R> f = (Func6<Object, Object, Object, Object, Object, Object, R>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Func6 and expected 6 arguments, but we received: " + args.length);
// }
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5]);
// } else if (function instanceof Func7) {
// Func7<Object, Object, Object, Object, Object, Object, Object, R> f = (Func7<Object, Object, Object, Object, Object, Object, Object, R>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Func7 and expected 7 arguments, but we received: " + args.length);
// }
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
// } else if (function instanceof Func8) {
// Func8<Object, Object, Object, Object, Object, Object, Object, Object, R> f = (Func8<Object, Object, Object, Object, Object, Object, Object, Object, R>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Func8 and expected 8 arguments, but we received: " + args.length);
// }
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
// } else if (function instanceof Func9) {
// Func9<Object, Object, Object, Object, Object, Object, Object, Object, Object, R> f = (Func9<Object, Object, Object, Object, Object, Object, Object, Object, Object, R>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Func9 and expected 9 arguments, but we received: " + args.length);
// }
// return f.call(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
// } else if (function instanceof FuncN) {
// FuncN<R> f = (FuncN<R>) function;
// return f.call(args);
// } else if (function instanceof Action0) {
// Action0 f = (Action0) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Action0 and expected 0 arguments, but we received: " + args.length);
// }
// f.call();
// return null;
// } else if (function instanceof Action1) {
// Action1<Object> f = (Action1<Object>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length);
// }
// f.call(args[0]);
// return null;
// } else if (function instanceof Action2) {
// Action2<Object, Object> f = (Action2<Object, Object>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Action2 and expected 2 argument, but we received: " + args.length);
// }
// f.call(args[0], args[1]);
// return null;
// } else if (function instanceof Action3) {
// Action3<Object, Object, Object> f = (Action3<Object, Object, Object>) function;
// if (args.length != 1) {
// throw new RuntimeException("The closure was Action1 and expected 1 argument, but we received: " + args.length);
// }
// f.call(args[0], args[1], args[2]);
// return null;
// }
//
// throw new RuntimeException("Unknown implementation of Function: " + function.getClass().getSimpleName());
// }
@SuppressWarnings({ "unchecked", "rawtypes" })
private static FuncN fromFunction(Function function) {
// check Func* classes
if (function instanceof Func0) {
return fromFunc((Func0) function);
} else if (function instanceof Func1) {
return fromFunc((Func1) function);
} else if (function instanceof Func2) {
return fromFunc((Func2) function);
} else if (function instanceof Func3) {
return fromFunc((Func3) function);
} else if (function instanceof Func4) {
return fromFunc((Func4) function);
} else if (function instanceof Func5) {
return fromFunc((Func5) function);
} else if (function instanceof Func6) {
return fromFunc((Func6) function);
} else if (function instanceof Func7) {
return fromFunc((Func7) function);
} else if (function instanceof Func8) {
return fromFunc((Func8) function);
} else if (function instanceof Func9) {
return fromFunc((Func9) function);
} else if (function instanceof FuncN) {
return (FuncN) function;
} else if (function instanceof Action0) {
return fromAction((Action0) function);
} else if (function instanceof Action1) {
return fromAction((Action1) function);
} else if (function instanceof Action2) {
return fromAction((Action2) function);
} else if (function instanceof Action3) {
return fromAction((Action3) function);
}
throw new RuntimeException("Unknown implementation of Function: " + function.getClass().getSimpleName());
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <R> FuncN<R> fromFunc(final Func0<R> f) {
return new FuncN<R>() {
@Override
public R call(Object... args) {
if (args.length != 0) {
throw new RuntimeException("Func0 expecting 0 arguments.");
}
return f.call();
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, R> FuncN<R> fromFunc(final Func1<T0, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 1) {
throw new RuntimeException("Func1 expecting 1 argument.");
}
return f.call((T0) args[0]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, R> FuncN<R> fromFunc(final Func2<T0, T1, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 2) {
throw new RuntimeException("Func2 expecting 2 arguments.");
}
return f.call((T0) args[0], (T1) args[1]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2, R> FuncN<R> fromFunc(final Func3<T0, T1, T2, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 3) {
throw new RuntimeException("Func3 expecting 3 arguments.");
}
return f.call((T0) args[0], (T1) args[1], (T2) args[2]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2, T3, R> FuncN<R> fromFunc(final Func4<T0, T1, T2, T3, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 4) {
throw new RuntimeException("Func4 expecting 4 arguments.");
}
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2, T3, T4, R> FuncN<R> fromFunc(final Func5<T0, T1, T2, T3, T4, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 5) {
throw new RuntimeException("Func5 expecting 5 arguments.");
}
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2, T3, T4, T5, R> FuncN<R> fromFunc(final Func6<T0, T1, T2, T3, T4, T5, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 6) {
throw new RuntimeException("Func6 expecting 6 arguments.");
}
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2, T3, T4, T5, T6, R> FuncN<R> fromFunc(final Func7<T0, T1, T2, T3, T4, T5, T6, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 7) {
throw new RuntimeException("Func7 expecting 7 arguments.");
}
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2, T3, T4, T5, T6, T7, R> FuncN<R> fromFunc(final Func8<T0, T1, T2, T3, T4, T5, T6, T7, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 8) {
throw new RuntimeException("Func8 expecting 8 arguments.");
}
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6], (T7) args[7]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2, T3, T4, T5, T6, T7, T8, R> FuncN<R> fromFunc(final Func9<T0, T1, T2, T3, T4, T5, T6, T7, T8, R> f) {
return new FuncN<R>() {
@SuppressWarnings("unchecked")
@Override
public R call(Object... args) {
if (args.length != 9) {
throw new RuntimeException("Func9 expecting 9 arguments.");
}
return f.call((T0) args[0], (T1) args[1], (T2) args[2], (T3) args[3], (T4) args[4], (T5) args[5], (T6) args[6], (T7) args[7], (T8) args[8]);
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static FuncN<Void> fromAction(final Action0 f) {
return new FuncN<Void>() {
@Override
public Void call(Object... args) {
if (args.length != 0) {
throw new RuntimeException("Action0 expecting 0 arguments.");
}
f.call();
return null;
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0> FuncN<Void> fromAction(final Action1<T0> f) {
return new FuncN<Void>() {
@SuppressWarnings("unchecked")
@Override
public Void call(Object... args) {
if (args.length != 1) {
throw new RuntimeException("Action1 expecting 1 argument.");
}
f.call((T0) args[0]);
return null;
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1> FuncN<Void> fromAction(final Action2<T0, T1> f) {
return new FuncN<Void>() {
@SuppressWarnings("unchecked")
@Override
public Void call(Object... args) {
if (args.length != 2) {
throw new RuntimeException("Action3 expecting 2 arguments.");
}
f.call((T0) args[0], (T1) args[1]);
return null;
}
};
}
/**
* Convert a function to FuncN to allow heterogeneous handling of functions with different arities.
*
* @param f
* @return {@link FuncN}
*/
public static <T0, T1, T2> FuncN<Void> fromAction(final Action3<T0, T1, T2> f) {
return new FuncN<Void>() {
@SuppressWarnings("unchecked")
@Override
public Void call(Object... args) {
if (args.length != 3) {
throw new RuntimeException("Action3 expecting 3 arguments.");
}
f.call((T0) args[0], (T1) args[1], (T2) args[2]);
return null;
}
};
}
@SuppressWarnings("unchecked")
public static <T> Func1<T, Boolean> alwaysTrue() {
return (Func1<T, Boolean>) AlwaysTrue.INSTANCE;
}
@SuppressWarnings("unchecked")
public static <T> Func1<T, T> identity() {
return (Func1<T, T>) Identity.INSTANCE;
}
private enum AlwaysTrue implements Func1<Object, Boolean> {
INSTANCE;
@Override
public Boolean call(Object o) {
return true;
}
}
private enum Identity implements Func1<Object, Object> {
INSTANCE;
@Override
public Object call(Object o) {
return o;
}
}
}