Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
reactor.net.AbstractNetChannel Maven / Gradle / Ivy
package reactor.net;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Environment;
import reactor.core.Reactor;
import reactor.core.composable.Deferred;
import reactor.core.composable.Promise;
import reactor.core.composable.Stream;
import reactor.core.composable.spec.Promises;
import reactor.core.support.NotifyConsumer;
import reactor.event.Event;
import reactor.event.dispatch.Dispatcher;
import reactor.event.registry.Registration;
import reactor.event.selector.Selector;
import reactor.event.selector.Selectors;
import reactor.event.support.EventConsumer;
import reactor.function.Consumer;
import reactor.function.Function;
import reactor.function.batch.BatchConsumer;
import reactor.io.Buffer;
import reactor.io.encoding.Codec;
import reactor.queue.BlockingQueueFactory;
import reactor.util.Assert;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.NoSuchElementException;
import java.util.Queue;
import static reactor.event.selector.Selectors.$;
/**
* An abstract {@link reactor.net.NetChannel} implementation that handles the basic interaction and {@link
* reactor.core.composable.Stream} and {@link reactor.function.Consumer} handling.
*
* @author Jon Brisbin
*/
public abstract class AbstractNetChannel implements NetChannel {
protected final Logger log = LoggerFactory.getLogger(getClass());
private final Selector read = $();
private final Environment env;
private final Reactor ioReactor;
private final Reactor eventsReactor;
private final Codec codec;
private final Function decoder;
private final Function encoder;
private final Queue replyToKeys;
protected AbstractNetChannel(@Nonnull Environment env,
@Nullable Codec codec,
@Nonnull Dispatcher ioDispatcher,
@Nonnull Reactor eventsReactor) {
Assert.notNull(env, "IO Dispatcher cannot be null");
Assert.notNull(env, "Events Reactor cannot be null");
this.env = env;
this.ioReactor = new Reactor(ioDispatcher,
null,
eventsReactor.getDispatchErrorHandler(),
eventsReactor.getUncaughtErrorHandler());
this.eventsReactor = new Reactor(eventsReactor.getDispatcher(),
null,
eventsReactor.getDispatchErrorHandler(),
eventsReactor.getUncaughtErrorHandler());
this.eventsReactor.getConsumerRegistry().clear();
for (Registration extends Consumer extends Event>>> reg : eventsReactor.getConsumerRegistry()) {
this.eventsReactor.getConsumerRegistry().register(reg.getSelector(), reg.getObject());
}
this.codec = codec;
if (null != codec) {
this.decoder = codec.decoder(new NotifyConsumer(read.getObject(), this.eventsReactor));
this.encoder = codec.encoder();
} else {
this.decoder = null;
this.encoder = null;
}
this.replyToKeys = BlockingQueueFactory.createQueue();
consume(new Consumer() {
@Override
public void accept(IN in) {
try {
if (!replyToKeys.isEmpty()) {
AbstractNetChannel.this.eventsReactor.notify(replyToKeys.remove(), Event.wrap(in));
}
} catch (NoSuchElementException ignored) {
}
}
});
}
public Function getDecoder() {
return decoder;
}
public Function getEncoder() {
return encoder;
}
@Override
public Stream in() {
final Deferred> d = new Deferred>(new Stream(eventsReactor, -1, null, env));
consume(new Consumer() {
@Override
public void accept(IN in) {
d.accept(in);
}
});
return d.compose();
}
@Override
public BatchConsumer out() {
return new WriteConsumer(null);
}
@Override
public NetChannel when(Class errorType, Consumer errorConsumer) {
eventsReactor.on(Selectors.T(errorType), new EventConsumer(errorConsumer));
return this;
}
@Override
public NetChannel consume(final Consumer consumer) {
eventsReactor.on(read, new Consumer>() {
@Override
public void accept(Event ev) {
consumer.accept(ev.getData());
}
});
return this;
}
@Override
public NetChannel receive(final Function fn) {
consume(new Consumer() {
@Override
public void accept(IN in) {
send(fn.apply(in));
}
});
return this;
}
@Override
public NetChannel send(Stream data) {
data.consume(new Consumer() {
@Override
public void accept(OUT out) {
send(out, null);
}
});
return this;
}
@Override
public Promise send(OUT data) {
Deferred> d = Promises.defer(env, eventsReactor.getDispatcher());
send(data, d);
return d.compose();
}
@Override
public NetChannel sendAndForget(OUT data) {
send(data, null);
return this;
}
@Override
public Promise sendAndReceive(OUT data) {
final Deferred> d = Promises.defer(env, eventsReactor.getDispatcher());
Selector sel = $();
eventsReactor.on(sel, new EventConsumer(d)).cancelAfterUse();
replyToKeys.add(sel.getObject());
send(data, null);
return d.compose();
}
@Override
public Promise close() {
Deferred> d = Promises.defer(getEnvironment(), eventsReactor.getDispatcher());
eventsReactor.getConsumerRegistry().unregister(read.getObject());
close(d);
return d.compose();
}
/**
* Send data on this connection. The current codec (if any) will be used to encode the data to a {@link
* reactor.io.Buffer}. The given callback will be invoked when the write has completed.
*
* @param data
* The outgoing data.
* @param onComplete
* The callback to invoke when the write is complete.
*/
protected void send(OUT data, final Deferred> onComplete) {
ioReactor.schedule(new WriteConsumer(onComplete), data);
}
/**
* Performing necessary decoding on the data and notify the internal {@link Reactor} of any results.
*
* @param data
* The data to decode.
*
* @return {@literal true} if any more data is remaining to be consumed in the given {@link Buffer}, {@literal false}
* otherwise.
*/
public boolean read(Buffer data) {
if (null != decoder && null != data.byteBuffer()) {
decoder.apply(data);
} else {
eventsReactor.notify(read.getObject(), Event.wrap(data));
}
return data.remaining() > 0;
}
public void notifyRead(Object obj) {
eventsReactor.notify(read.getObject(), (Event.class.isInstance(obj) ? (Event) obj : Event.wrap(obj)));
}
public void notifyError(Throwable throwable) {
eventsReactor.notify(throwable.getClass(), Event.wrap(throwable));
}
/**
* Subclasses must implement this method to perform the actual IO of writing data to the connection.
*
* @param data
* The data to write, as a {@link Buffer}.
* @param onComplete
* The callback to invoke when the write is complete.
*/
protected void write(Buffer data, Deferred> onComplete, boolean flush) {
write(data.byteBuffer(), onComplete, flush);
}
/**
* Subclasses must implement this method to perform the actual IO of writing data to the connection.
*
* @param data
* The data to write.
* @param onComplete
* The callback to invoke when the write is complete.
* @param flush
* whether to flush the underlying IO channel
*/
protected abstract void write(ByteBuffer data, Deferred> onComplete, boolean flush);
/**
* Subclasses must implement this method to perform the actual IO of writing data to the connection.
*
* @param data
* The data to write.
* @param onComplete
* The callback to invoke when the write is complete.
* @param flush
* whether to flush the underlying IO channel
*/
protected abstract void write(Object data, Deferred> onComplete, boolean flush);
/**
* Subclasses must implement this method to perform IO flushes.
*/
protected abstract void flush();
protected Environment getEnvironment() {
return env;
}
protected Reactor getEventsReactor() {
return eventsReactor;
}
protected Reactor getIoReactor() {
return ioReactor;
}
private final class WriteConsumer implements BatchConsumer {
private final Deferred> onComplete;
private volatile boolean autoflush = true;
private WriteConsumer(Deferred> onComplete) {
this.onComplete = onComplete;
}
@Override
public void start() {
autoflush = false;
}
@Override
public void end() {
flush();
autoflush = true;
}
@Override
public void accept(OUT data) {
try {
if (null != encoder) {
Buffer bytes = encoder.apply(data);
if (bytes.remaining() > 0) {
write(bytes, onComplete, autoflush);
}
} else {
if (Buffer.class.isInstance(data)) {
write((Buffer) data, onComplete, autoflush);
} else {
write(data, onComplete, autoflush);
}
}
} catch (Throwable t) {
eventsReactor.notify(t.getClass(), Event.wrap(t));
if (null != onComplete) {
onComplete.accept(t);
}
}
}
}
}