Package reactor.net

Source Code of reactor.net.AbstractNetChannel$WriteConsumer

/*
* Copyright (c) 2011-2014 Pivotal Software, 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 reactor.net;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Environment;
import reactor.core.Reactor;
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.rx.Promise;
import reactor.rx.Promises;
import reactor.rx.Stream;
import reactor.rx.Streams;
import reactor.rx.stream.HotStream;
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.rx.Stream} and {@link reactor.function.Consumer} handling.
*
* @author Jon Brisbin
* @author Stephane Maldini
*/
public abstract class AbstractNetChannel<IN, OUT> implements NetChannel<IN, OUT> {

  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<Buffer, IN, OUT> codec;
  private final Function<Buffer, IN>   decoder;
  private final Function<OUT, Buffer>  encoder;
  private final Queue<Object>          replyToKeys;

  protected AbstractNetChannel(@Nonnull Environment env,
                               @Nullable Codec<Buffer, IN, OUT> 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<?>> reg : eventsReactor.getConsumerRegistry()) {
      this.eventsReactor.getConsumerRegistry().register(reg.getSelector(), reg.getObject());
    }
    this.codec = codec;
    if (null != codec) {
      this.decoder = codec.decoder(new NotifyConsumer<IN>(read.getObject(), this.eventsReactor));
      this.encoder = codec.encoder();
    } else {
      this.decoder = null;
      this.encoder = null;
    }
    this.replyToKeys = BlockingQueueFactory.createQueue();

    consume(new Consumer<IN>() {
      @Override
      public void accept(IN in) {
        try {
          if (!replyToKeys.isEmpty()) {
            AbstractNetChannel.this.eventsReactor.notify(replyToKeys.remove(), Event.wrap(in));
          }
        } catch (NoSuchElementException ignored) {
        }
      }
    });
  }

  public Function<Buffer, IN> getDecoder() {
    return decoder;
  }

  public Function<OUT, Buffer> getEncoder() {
    return encoder;
  }

  @Override
  public Stream<IN> in() {
    final HotStream<IN> d = Streams.<IN>defer(env, eventsReactor.getDispatcher());
    consume(new Consumer<IN>() {
      @Override
      public void accept(IN in) {
        d.broadcastNext(in);
      }
    });
    return d;
  }

  @Override
  public BatchConsumer<OUT> out() {
    return new WriteConsumer(null);
  }

  @Override
  public <T extends Throwable> NetChannel<IN, OUT> when(Class<T> errorType, Consumer<T> errorConsumer) {
    eventsReactor.on(Selectors.T(errorType), new EventConsumer<T>(errorConsumer));
    return this;
  }

  @Override
  public NetChannel<IN, OUT> consume(final Consumer<IN> consumer) {
    eventsReactor.on(read, new Consumer<Event<IN>>() {
      @Override
      public void accept(Event<IN> ev) {
        consumer.accept(ev.getData());
      }
    });
    return this;
  }

  @Override
  public NetChannel<IN, OUT> receive(final Function<IN, OUT> fn) {
    consume(new Consumer<IN>() {
      @Override
      public void accept(IN in) {
        send(fn.apply(in));
      }
    });
    return this;
  }

  @Override
  public NetChannel<IN, OUT> send(Stream<OUT> data) {
    data.consume(new Consumer<OUT>() {
      @Override
      public void accept(OUT out) {
        send(out, null);
      }
    });
    return this;
  }

  @Override
  public Promise<Void> send(OUT data) {
    Promise<Void> d = Promises.defer(env, eventsReactor.getDispatcher());
    send(data, d);
    return d;
  }

  @Override
  public NetChannel<IN, OUT> sendAndForget(OUT data) {
    send(data, null);
    return this;
  }

  @Override
  public Promise<IN> sendAndReceive(OUT data) {
    final Promise<IN> d = Promises.defer(env, eventsReactor.getDispatcher());
    Selector sel = $();
    eventsReactor.on(sel, new EventConsumer<IN>(d)).cancelAfterUse();
    replyToKeys.add(sel.getObject());
    send(data, null);
    return d;
  }

  @Override
  public Promise<Boolean> close() {
    Promise<Boolean> d = Promises.defer(getEnvironment(), eventsReactor.getDispatcher());
    eventsReactor.getConsumerRegistry().unregister(read.getObject());
    close(d);
    return d;
  }

  /**
   * 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 Promise<Void> 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, Promise<Void> 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, Promise<Void> 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, Promise<Void> 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<OUT> {
    private final Promise<Void> onComplete;
    private volatile boolean autoflush = true;

    private WriteConsumer(Promise<Void> 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.onError(t);
        }
      }
    }
  }

}
TOP

Related Classes of reactor.net.AbstractNetChannel$WriteConsumer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.