
reactor.rx.action.aggregation.WindowShiftAction Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2015 Pivotal Software Inc., Inc. All Rights Reserved.
*
* 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 reactor.rx.action.aggregation;
import org.reactivestreams.Subscription;
import reactor.Environment;
import reactor.core.Dispatcher;
import reactor.fn.Consumer;
import reactor.fn.Pausable;
import reactor.fn.timer.Timer;
import reactor.rx.Stream;
import reactor.rx.action.Action;
import reactor.rx.broadcast.Broadcaster;
import reactor.rx.subscription.ReactiveSubscription;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* WindowAction is forwarding events on a steam until {@param backlog} is reached,
* after that streams collected events further, complete it and create a fresh new stream.
*
* @author Stephane Maldini
* @since 2.0
*/
public class WindowShiftAction extends Action> {
private final Consumer timeshiftTask;
private final List> currentWindows = new LinkedList<>();
private final int skip;
private final int batchSize;
private final long timeshift;
private final TimeUnit unit;
private final Timer timer;
private final Environment environment;
private final Dispatcher dispatcher;
private int index;
private Pausable timeshiftRegistration;
public WindowShiftAction(Environment environment, Dispatcher dispatcher, int size, int skip) {
this(environment, dispatcher, size, skip, -1l, -1l, null, null);
}
public WindowShiftAction(Environment environment, final Dispatcher dispatcher, int size, int skip,
final long timespan, final long timeshift, TimeUnit unit, final Timer timer) {
this.dispatcher = dispatcher;
this.skip = skip;
this.environment = environment;
this.batchSize = size;
if (timespan > 0 && timeshift > 0) {
final TimeUnit targetUnit = unit != null ? unit : TimeUnit.SECONDS;
final Consumer> flushTimerTask = new Consumer>() {
@Override
public void accept(ReactiveSubscription bucket) {
Iterator> it = currentWindows.iterator();
while (it.hasNext()) {
ReactiveSubscription itBucket = it.next();
if (bucket == itBucket) {
it.remove();
bucket.onComplete();
break;
}
}
}
};
this.timeshiftTask = new Consumer() {
@Override
public void accept(Long aLong) {
if (!isPublishing()) {
return;
}
dispatcher.dispatch(null, new Consumer() {
@Override
public void accept(Void aVoid) {
final ReactiveSubscription bucket = createWindowStream();
timer.submit(new Consumer() {
@Override
public void accept(Long aLong) {
dispatcher.dispatch(bucket, flushTimerTask, null);
}
}, timespan, targetUnit);
}
}, null);
}
};
this.timeshift = timeshift;
this.unit = targetUnit;
this.timer = timer;
} else {
this.timeshift = -1l;
this.unit = null;
this.timer = null;
this.timeshiftTask = null;
}
}
@Override
protected void doOnSubscribe(Subscription subscription) {
if(timer != null) {
timeshiftRegistration = timer.schedule(timeshiftTask,
timeshift,
unit);
}
}
@Override
protected void doNext(T value) {
if (timer == null && index++ % skip == 0) {
createWindowStream();
}
flushCallback(value);
}
@Override
protected void doComplete() {
for (ReactiveSubscription bucket : currentWindows) {
bucket.onComplete();
}
currentWindows.clear();
super.doComplete();
}
private void flushCallback(T event) {
Iterator> it = currentWindows.iterator();
while (it.hasNext()) {
ReactiveSubscription bucket = it.next();
bucket.onNext(event);
if (bucket.currentNextSignals() == batchSize) {
it.remove();
bucket.onComplete();
}
}
}
protected ReactiveSubscription createWindowStream() {
Action action = Broadcaster.create(environment, dispatcher);
ReactiveSubscription _currentWindow = new ReactiveSubscription(null, action);
currentWindows.add(_currentWindow);
action.onSubscribe(_currentWindow);
broadcastNext(action);
return _currentWindow;
}
@Override
public final Environment getEnvironment() {
return this.environment;
}
@Override
public final Dispatcher getDispatcher() {
return this.dispatcher;
}
@Override
protected void doError(Throwable ev) {
super.doError(ev);
for (ReactiveSubscription bucket : currentWindows) {
bucket.onError(ev);
}
currentWindows.clear();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy