@Test
  public void workerOrchestrator() throws InterruptedException {
    Reactor reactor = Reactors.reactor(env, Environment.WORK_QUEUE);

    CountDownLatch latch = new CountDownLatch(3);

    reactor.on(
        Selectors.$("worker"),
        new Consumer() {
          @Override
          public void accept(Object o) {
            System.out.println(Thread.currentThread().getName() + " worker " + o);
            reactor.notify("orchestrator", Event.wrap(1000));
            latch.countDown();
            System.out.println(Thread.currentThread().getName() + " ok");
          }
        });

    reactor.on(
        Selectors.$("orchestrator"),
        new Consumer<Event<Integer>>() {

          @Override
          public void accept(Event<Integer> event) {
            sendTask();
          }

          void sendTask() {
            System.out.println(Thread.currentThread().getName() + " sendTask ");
            reactor.notify("worker", Event.wrap(latch.getCount()));
            latch.countDown();
          }
        });

    reactor.notify("orchestrator", Event.wrap(1000));

    Assert.isTrue(latch.await(10, TimeUnit.SECONDS));
  }
 @Override
 public <V, X extends Throwable> StateMachine<T> when(
     Class<X> errorType, final Consumer<Tuple3<V, T, X>> errorConsumer) {
   observable.on(
       Selectors.type(errorType),
       new Consumer<Event<Throwable>>() {
         @SuppressWarnings("unchecked")
         @Override
         public void accept(Event<Throwable> ev) {
           if (null != errorConsumer && ev.getData() instanceof StateException) {
             V state = (V) ((StateException) ev.getData()).state;
             X cause = (X) ev.getData().getCause();
             T data = (T) ((StateException) ev.getData()).data;
             errorConsumer.accept(Tuple.of(state, data, cause));
           }
         }
       });
   return this;
 }
 @Override
 public <V> StateMachine<T> on(final V state, final Function<T, T> fn) {
   if (strict) {
     Assert.isTrue(states.contains(state), "State " + state + " has not been defined");
   }
   observable.on(
       (state instanceof Selector ? (Selector) state : Selectors.object(state)),
       new Consumer<Event<T>>() {
         @SuppressWarnings("unchecked")
         @Override
         public void accept(final Event<T> data) {
           try {
             data.setData(fn.apply(data.getData()));
           } catch (Throwable t) {
             observable.notify(
                 t.getClass(), Event.wrap(new StateException(t, state, data.getData())));
           }
         }
       });
   return this;
 }
Exemple #4
0
 /**
  * Subclasses should register the given {@link reactor.net.NetChannel} for later use.
  *
  * @param ioChannel The channel object.
  * @param netChannel The {@link NetChannel}.
  * @param <C> The type of the channel object.
  * @return {@link reactor.event.registry.Registration} of this channel in the {@link Registry}.
  */
 protected <C> Registration<? extends NetChannel<IN, OUT>> register(
     @Nonnull C ioChannel, @Nonnull NetChannel<IN, OUT> netChannel) {
   Assert.notNull(ioChannel, "Channel cannot be null.");
   Assert.notNull(netChannel, "NetChannel cannot be null.");
   return netChannels.register(Selectors.$(ioChannel), netChannel);
 }
Exemple #5
0
/**
 * Abstract base class that implements common functionality shared by clients and servers.
 *
 * @author Jon Brisbin
 */
public abstract class AbstractNetPeer<IN, OUT> {

  private final Registry<NetChannel<IN, OUT>> netChannels =
      new CachingRegistry<NetChannel<IN, OUT>>();
  private final Event<AbstractNetPeer<IN, OUT>> selfEvent = Event.wrap(this);
  private final Selector open = Selectors.$();
  private final Selector close = Selectors.$();
  private final Selector start = Selectors.$();
  private final Selector shutdown = Selectors.$();

  private final Environment env;
  private final Reactor reactor;
  private final Codec<Buffer, IN, OUT> codec;
  private final Collection<Consumer<NetChannel<IN, OUT>>> consumers;

  protected AbstractNetPeer(
      @Nonnull Environment env,
      @Nonnull Reactor reactor,
      @Nullable Codec<Buffer, IN, OUT> codec,
      @Nonnull Collection<Consumer<NetChannel<IN, OUT>>> consumers) {
    this.env = env;
    this.reactor = reactor;
    this.codec = codec;
    this.consumers = consumers;

    for (final Consumer<NetChannel<IN, OUT>> consumer : consumers) {
      reactor.on(
          open,
          new Consumer<Event<NetChannel<IN, OUT>>>() {
            @Override
            public void accept(Event<NetChannel<IN, OUT>> ev) {
              consumer.accept(ev.getData());
            }
          });
    }
  }

  public Promise<Boolean> close() {
    Deferred<Boolean, Promise<Boolean>> d = Promises.defer(env, reactor.getDispatcher());
    close(d);
    return d.compose();
  }

  public void close(@Nullable final Consumer<Boolean> onClose) {
    reactor.schedule(
        new Consumer<Void>() {
          @Override
          public void accept(Void v) {
            for (Registration<? extends NetChannel<IN, OUT>> reg : getChannels()) {
              if (null == reg) {
                continue;
              }
              doCloseChannel(reg.getObject());
            }
            getChannels().clear();
            doClose(onClose);
          }
        },
        null);
  }

  /**
   * Subclasses should register the given {@link reactor.net.NetChannel} for later use.
   *
   * @param ioChannel The channel object.
   * @param netChannel The {@link NetChannel}.
   * @param <C> The type of the channel object.
   * @return {@link reactor.event.registry.Registration} of this channel in the {@link Registry}.
   */
  protected <C> Registration<? extends NetChannel<IN, OUT>> register(
      @Nonnull C ioChannel, @Nonnull NetChannel<IN, OUT> netChannel) {
    Assert.notNull(ioChannel, "Channel cannot be null.");
    Assert.notNull(netChannel, "NetChannel cannot be null.");
    return netChannels.register(Selectors.$(ioChannel), netChannel);
  }

  /**
   * Find the {@link NetChannel} for the given IO channel object.
   *
   * @param ioChannel The channel object.
   * @param <C> The type of the channel object.
   * @return The {@link NetChannel} associated with the given channel.
   */
  protected <C> NetChannel<IN, OUT> select(@Nonnull C ioChannel) {
    Assert.notNull(ioChannel, "Channel cannot be null.");
    Iterator<Registration<? extends NetChannel<IN, OUT>>> channs =
        netChannels.select(ioChannel).iterator();
    if (channs.hasNext()) {
      return channs.next().getObject();
    } else {
      NetChannel<IN, OUT> conn = createChannel(ioChannel);
      register(ioChannel, conn);
      notifyOpen(conn);
      return conn;
    }
  }

  /**
   * Close the given channel.
   *
   * @param channel The channel object.
   * @param <C> The type of the channel object.
   */
  protected <C> void close(@Nonnull C channel) {
    Assert.notNull(channel, "Channel cannot be null");
    for (Registration<? extends NetChannel<IN, OUT>> reg : netChannels.select(channel)) {
      NetChannel<IN, OUT> chann = reg.getObject();
      reg.cancel();
      notifyClose(chann);
    }
  }

  /**
   * Subclasses should implement this method and provide a {@link NetChannel} object.
   *
   * @param ioChannel The IO channel object to associate with this {@link reactor.net.NetChannel}.
   * @param <C> The type of the channel object.
   * @return The new {@link NetChannel} object.
   */
  protected abstract <C> NetChannel<IN, OUT> createChannel(C ioChannel);

  /** Notify this server's consumers that the server has started. */
  protected void notifyStart(final Runnable started) {
    getReactor().notify(start.getObject(), selfEvent);
    if (null != started) {
      getReactor()
          .schedule(
              new Consumer<Runnable>() {
                @Override
                public void accept(Runnable r) {
                  r.run();
                }
              },
              started);
    }
  }

  /**
   * Notify this client's consumers than a global error has occurred.
   *
   * @param error The error to notify.
   */
  protected void notifyError(@Nonnull Throwable error) {
    Assert.notNull(error, "Error cannot be null.");
    reactor.notify(error.getClass(), Event.wrap(error));
  }

  /**
   * Notify this peer's consumers that the channel has been opened.
   *
   * @param channel The channel that was opened.
   */
  protected void notifyOpen(@Nonnull NetChannel<IN, OUT> channel) {
    reactor.notify(open.getObject(), Event.wrap(channel));
  }

  /**
   * Notify this peer's consumers that the given channel has been closed.
   *
   * @param channel The channel that was closed.
   */
  protected void notifyClose(@Nonnull NetChannel<IN, OUT> channel) {
    reactor.notify(close.getObject(), Event.wrap(channel));
  }

  /** Notify this server's consumers that the server has stopped. */
  protected void notifyShutdown() {
    getReactor().notify(shutdown.getObject(), selfEvent);
  }

  /**
   * Get the {@link Codec} in use.
   *
   * @return The codec. May be {@literal null}.
   */
  @Nullable
  protected Codec<Buffer, IN, OUT> getCodec() {
    return codec;
  }

  @Nonnull
  protected Environment getEnvironment() {
    return env;
  }

  @Nonnull
  protected Reactor getReactor() {
    return reactor;
  }

  @Nonnull
  protected Collection<Consumer<NetChannel<IN, OUT>>> getConsumers() {
    return consumers;
  }

  @Nonnull
  protected Registry<NetChannel<IN, OUT>> getChannels() {
    return netChannels;
  }

  /**
   * Subclasses should implement this method to perform the actual IO channel close.
   *
   * @param onClose
   */
  protected void doClose(@Nullable Consumer<Boolean> onClose) {
    getReactor().schedule(onClose, null);
  }

  /**
   * Close the given channel.
   *
   * @param channel
   */
  protected void doCloseChannel(NetChannel<IN, OUT> channel) {
    channel.close();
  }
}
  public static void main(String[] args) throws Exception {
    Environment env = new Environment();
    final TradeServer server = new TradeServer();

    // Use a Reactor to dispatch events using the high-speed Dispatcher
    final Reactor serverReactor = Reactors.reactor(env);

    // Create a single key and Selector for efficiency
    final Selector tradeExecute = Selectors.object("trade.execute");

    // For each Trade event, execute that on the server and notify connected clients
    // because each client that connects links to the serverReactor
    serverReactor.on(
        tradeExecute,
        (Event<Trade> ev) -> {
          server.execute(ev.getData());

          // Since we're async, for this test, use a latch to tell when we're done
          latch.countDown();
        });

    @SuppressWarnings("serial")
    WebSocketServlet wss =
        new WebSocketServlet() {
          @Override
          public void configure(WebSocketServletFactory factory) {
            factory.setCreator(
                (req, resp) ->
                    new WebSocketListener() {
                      AtomicLong counter = new AtomicLong();

                      @Override
                      public void onWebSocketBinary(byte[] payload, int offset, int len) {}

                      @Override
                      public void onWebSocketClose(int statusCode, String reason) {}

                      @Override
                      public void onWebSocketConnect(final Session session) {
                        LOG.info("Connected a websocket client: {}", session);

                        // Keep track of a rolling average
                        final AtomicReference<Float> avg = new AtomicReference<>(0f);

                        serverReactor.on(
                            tradeExecute,
                            (Event<Trade> ev) -> {
                              Trade t = ev.getData();
                              avg.set((avg.get() + t.getPrice()) / 2);

                              // Send a message every 1000th trade.
                              // Otherwise, we completely overwhelm the browser and network.
                              if (counter.incrementAndGet() % 1000 == 0) {
                                try {
                                  session
                                      .getRemote()
                                      .sendString(String.format("avg: %s", avg.get()));
                                } catch (IOException e) {
                                  if (!"Failed to write bytes".equals(e.getMessage())) {
                                    e.printStackTrace();
                                  }
                                }
                              }
                            });
                      }

                      @Override
                      public void onWebSocketError(Throwable cause) {}

                      @Override
                      public void onWebSocketText(String message) {}
                    });
          }
        };
    serve(wss);

    LOG.info(
        "Connect websocket clients now (waiting for 10 seconds).\n"
            + "Open websocket/src/main/webapp/ws.html in a browser...");
    Thread.sleep(10000);

    // Start a throughput timer
    startTimer();

    // Publish one event per trade
    for (int i = 0; i < totalTrades; i++) {
      // Pull next randomly-generated Trade from server
      Trade t = server.nextTrade();

      // Notify the Reactor the event is ready to be handled
      serverReactor.notify(tradeExecute.getObject(), Event.wrap(t));
    }

    // Stop throughput timer and output metrics
    endTimer();

    server.stop();
  }