Пример #1
0
  // #my-actor
  class MyActor extends UntypedActor {
    LoggingAdapter log = Logging.getLogger(getContext().system(), this);

    @Override
    public void preStart() {
      log.debug("Starting");
    }

    @Override
    public void preRestart(Throwable reason, Option<Object> message) {
      log.error(
          reason,
          "Restarting due to [{}] when processing [{}]",
          reason.getMessage(),
          message.isDefined() ? message.get() : "");
    }

    public void onReceive(Object message) {
      if (message.equals("test")) {
        log.info("Received test");
      } else {
        log.warning("Received unknown message: {}", message);
      }
    }
  }
  /** Actor system singleton for this application. */
  @Bean
  public ActorSystem actorSystem() {

    // load the Akka configuration
    String seedNodes = "[";
    for (String seed : clusterConfig.getSeedNodes()) {
      seedNodes += "\"akka.tcp://" + clusterConfig.getName() + "@" + seed.trim() + "\",";
    }
    seedNodes += "]";

    final Config applicationConf = ConfigFactory.load();
    final Config config =
        ConfigFactory.parseString("akka.cluster.seed-nodes=" + seedNodes)
            .withFallback(
                ConfigFactory.parseString(
                    "akka.remote.netty.tcp.bind-port=" + clusterConfig.getLocalPort()))
            .withFallback(
                ConfigFactory.parseString(
                    "akka.remote.netty.tcp.hostname=" + clusterConfig.getNodeHost()))
            .withFallback(
                ConfigFactory.parseString(
                    "akka.remote.netty.tcp.port=" + clusterConfig.getLocalPort()))
            .withFallback(ConfigFactory.parseString("akka.cluster.roles=[matcher]"))
            .withFallback(ConfigFactory.load(applicationConf));

    ActorSystem system = ActorSystem.create(clusterConfig.getName(), config);
    LoggingAdapter log = Logging.getLogger(system, this);
    log.info("Using Akka system settings: " + system.settings().toString());

    // initialize the application context in the Akka Spring Extension
    SpringExtension.SpringExtProvider.get(system).initialize(applicationContext);
    return system;
  }
Пример #3
0
/**
 * 계층구조 트리의 루트에 해당하는 최상위 부모 액터
 *
 * @author Baekjun Lim
 */
public class PingActor extends UntypedActor {

  private LoggingAdapter log = Logging.getLogger(getContext().system(), this);
  private ActorRef child;
  private int count = 0;

  public PingActor() {
    child = context().actorOf(Props.create(Ping1Actor.class), "ping1Actor");
  }

  @Override
  public void onReceive(Object message) throws Exception {
    if (message instanceof String) {
      String msg = (String) message;
      if ("work".equals(msg)) {
        child.tell(msg, getSelf());
      } else if ("done".equals(msg)) {
        if (count == 0) {
          count++;
        } else {
          log.info("all works are completed.");
          count = 0;
        }
      }
    }
  }
}
Пример #4
0
/** @author TTDKOC */
public class HelloWorld extends UntypedActor {
  private final LoggingAdapter LOG = Logging.getLogger(getContext().system(), this);

  @Override
  public void preStart() throws Exception {
    super.preStart();
    ActorRef greeter = getContext().actorOf(Props.create(Greeter.class), "greeter");
    greeter.tell(MSG.GREET, getSelf());
  }

  @Override
  public void aroundPostStop() {
    super.aroundPostStop();
    LOG.info("Stopping...");
  }

  /*
   * (non-Javadoc)
   *
   * @see akka.actor.UntypedActor#onReceive(java.lang.Object)
   */
  @Override
  public void onReceive(Object message) throws Exception {
    LOG.info("Started {}", message);
    Thread.sleep(3000);
    if (message == MSG.DONE) {
      getContext().stop(getSelf());
    }
    LOG.info("Completed {}", message);
  }
}
Пример #5
0
public class MetadataCursorMock
    extends StreamCursor<Iterator<Map.Entry<String, byte[]>>, MetadataItem> {

  private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  public MetadataCursorMock(Map<String, byte[]> metadataDocuments) {
    super(metadataDocuments.entrySet().iterator());
  }

  public static Props props(Map<String, byte[]> metadataDocuments) {
    return Props.create(MetadataCursorMock.class, metadataDocuments);
  }

  @Override
  protected boolean hasNext() throws Exception {
    log.debug("has next");

    return t.hasNext();
  }

  @Override
  protected Future<MetadataItem> next() {
    log.debug("next");

    Entry<String, byte[]> entry = t.next();
    return Futures.successful(new MetadataItem(entry.getKey(), entry.getValue()));
  }
}
Пример #6
0
  @Override
  public void onReceive(Object message) throws Exception {
    LoggingAdapter log = Logging.getLogger(getContext().system(), this);

    if (message instanceof Read) { // read
      log.info("Received read request");
      getSender().tell(new Object[] {tag, value}, getSelf());
    } else if (message instanceof Tag) { // read-tag
      log.info("Received read tag request");
      getSender().tell(new Tag(tag), getSelf());
    } else if (message instanceof Write) { // write
      final int tag = ((Write) message).getTag();
      final String value = ((Write) message).getValue();

      log.info("Received write request: {}", tag + " -> " + value);

      if (tag > this.tag) {
        this.tag = tag;
        this.value = value;
      }

    } else {
      unhandled(message);
    }
  }
Пример #7
0
/** @author: syedbahm Date: 8/6/14 */
public class ShardReadTransaction extends ShardTransaction {
  private final DOMStoreReadTransaction transaction;
  private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  public ShardReadTransaction(
      DOMStoreReadTransaction transaction, ActorRef shardActor, SchemaContext schemaContext) {
    super(shardActor, schemaContext);
    this.transaction = transaction;
  }

  public ShardReadTransaction(
      DOMStoreTransactionChain transactionChain,
      DOMStoreReadTransaction transaction,
      ActorRef shardActor,
      SchemaContext schemaContext) {
    super(transactionChain, shardActor, schemaContext);
    this.transaction = transaction;
  }

  @Override
  public void handleReceive(Object message) throws Exception {
    if (ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
      readData(transaction, ReadData.fromSerializable(message));
    } else {
      super.handleReceive(message);
    }
  }

  protected void closeTransaction(CloseTransaction message) {
    transaction.close();
    getSender().tell(new CloseTransactionReply().toSerializable(), getSelf());
    getSelf().tell(PoisonPill.getInstance(), getSelf());
  }
}
Пример #8
0
public class WorkerActor1 extends UntypedActor {
  LoggingAdapter log = Logging.getLogger(getContext().system(), this);
  private int state = 0;

  @Override
  public void preStart() {
    log.info("Starting WorkerActor1 instance hashcode # {}", this.hashCode());
  }

  public void onReceive(Object o) throws Exception {
    if (o == null) {
      throw new NullPointerException("Null Value Passed");
    } else if (o instanceof Integer) {
      Integer value = (Integer) o;
      if (value <= 0) {
        throw new ArithmeticException("Number equal or less than zero");
      } else state = value;
    } else if (o instanceof Result) {
      getSender().tell(state);
    } else {
      throw new IllegalArgumentException("Wrong Argument");
    }
  }

  @Override
  public void postStop() {
    log.info("Stopping WorkerActor1 instance hashcode # {}", this.hashCode());
  }
}
Пример #9
0
public class ClientConfigActor extends UntypedActor {
  private final SettingsImpl settings = Settings.SettingsProvider.get(getContext().system());
  private LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  public static Props props() {
    return Props.create(ClientConfigActor.class);
  }

  @Override
  public void onReceive(Object message) throws Exception {
    if (message instanceof ClientInfo) {
      ClientInfo clientInfo = (ClientInfo) message;
      final ClientConfig configFor = getConfigFor(clientInfo);
      log.debug(
          "config for client {} {} {}",
          clientInfo.id(),
          clientInfo.metrics().metadata().get("host-hostname"),
          configFor);
      getSender().tell(configFor, getSelf());
    } else {
      unhandled(message);
    }
  }

  private ClientConfig getConfigFor(ClientInfo clientInfo) {
    // take the first executor defined in the config
    return settings
        .getExecutors()
        .stream()
        .filter(ex -> clientInfo.executors().containsKey(ex))
        .findFirst()
        .map(executor -> getConfig(executor, clientInfo))
        .orElseGet(
            () -> {
              log.error("client has reported unsupported executors: {} ", clientInfo.executors());
              return ClientConfig.empty();
            });
  }

  private ClientConfig getConfig(String executor, ClientInfo clientInfo) {
    int executorCpuCount = clientInfo.executors().get(executor);
    int cpuSetSize = Math.min(executorCpuCount, settings.getCpuSetSize());
    // this happens if the config says use 2 cpus but the machine has only 1.
    if (executorCpuCount < settings.getCpuSetSize()) {
      log.warning(
          "Client reported less cpus ({}) than CPU_SET_SIZE ({})",
          executorCpuCount,
          settings.getCpuSetSize());
    }
    int numberOfExecutors = executorCpuCount / cpuSetSize;

    return ClientConfig.builder()
        .cpuSetSize(cpuSetSize)
        .executor(executor)
        .numberOfExecutors(numberOfExecutors)
        .build();
  }
}
Пример #10
0
public class MyUntypedActor extends UntypedActor {
  LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  public void onReceive(Object message) throws Exception {
    if (message instanceof String) {
      log.info("Received String message: {}", message);
      getSender().tell(message, getSelf());
    } else unhandled(message);
  }
}
Пример #11
0
  /**
   * @param args
   * @throws Exception
   */
  public static void main(String[] args) throws Exception {
    ActorSystem system = ActorSystem.create("faultTolerance");

    LoggingAdapter log = Logging.getLogger(system, system);

    Integer originalValue = Integer.valueOf(0);

    ActorRef supervisor = system.actorOf(new Props(SupervisorActor.class), "supervisor");

    log.info("Sending value 8, no exceptions should be thrown! ");
    supervisor.tell(Integer.valueOf(8));

    Integer result =
        (Integer)
            Await.result(
                Patterns.ask(supervisor, new Result(), 5000),
                Duration.create(5000, TimeUnit.MILLISECONDS));

    log.info("Value Received-> {}", result);
    assert result.equals(Integer.valueOf(8));

    log.info(
        "Sending value -8, ArithmeticException should be thrown! Our Supervisor strategy says resume !");
    supervisor.tell(Integer.valueOf(-8));

    result =
        (Integer)
            Await.result(
                Patterns.ask(supervisor, new Result(), 5000),
                Duration.create(5000, TimeUnit.MILLISECONDS));

    log.info("Value Received-> {}", result);
    assert result.equals(Integer.valueOf(8));

    log.info(
        "Sending value null, NullPointerException should be thrown! Our Supervisor strategy says restart !");
    supervisor.tell(null);

    result =
        (Integer)
            Await.result(
                Patterns.ask(supervisor, new Result(), 5000),
                Duration.create(5000, TimeUnit.MILLISECONDS));

    log.info("Value Received-> {}", result);
    assert originalValue.equals(result);

    log.info(
        "Sending value \"String\", IllegalArgumentException should be thrown! Our Supervisor strategy says Stop !");

    supervisor.tell(String.valueOf("Do Something"));

    log.info("Worker Actor shutdown !");
    system.shutdown();
  }
Пример #12
0
 public void onReceive(Object message) {
   if (message instanceof InitializeLogger) {
     getSender().tell(Logging.loggerInitialized(), getSelf());
   } else if (message instanceof Error) {
     // ...
   } else if (message instanceof Warning) {
     // ...
   } else if (message instanceof Info) {
     // ...
   } else if (message instanceof Debug) {
     // ...
   }
 }
Пример #13
0
/** @author pavels */
public class URLConnectWorker extends UntypedActor {

  LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  private int counter = 0;

  @Override
  public void onReceive(Object message) throws Exception {
    System.out.println("Receive message");
    if (message instanceof URLRequest) {
      log.info("URLConnect STARTING {}", message);
      this.counter++;
      URLRequest req = (URLRequest) message;
      log.info("\t Request URL :" + req.getUrl());
      getSender().tell(connect(req.getUrl(), req.getZaznamId()), getSelf());

    } else {
      unhandled(message);
    }
  }

  private URLResponse connect(String urlString, int zaznamId) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    HttpURLConnection httpUrlConn = null;
    URL url = null;
    try {
      url = new URL(urlString);
      httpUrlConn = (HttpURLConnection) (url).openConnection();
      httpUrlConn.setReadTimeout(2500);
      httpUrlConn.setConnectTimeout(2500);
      // jak na to ?
      httpUrlConn.setInstanceFollowRedirects(true);
      int respCode = httpUrlConn.getResponseCode();
      String respMessage = httpUrlConn.getResponseMessage();
      InputStream is = httpUrlConn.getInputStream();
      IOUtils.copyStreams(is, bos);

      return new URLResponse(
          respCode, respMessage, url, httpUrlConn.getURL(), zaznamId, bos.toByteArray());
    } catch (Exception ex) {
      log.error(ex, ex.getMessage());
      return new URLResponse(
          -1, ex.getMessage(), url, httpUrlConn.getURL(), zaznamId, bos.toByteArray());
    } finally {
      if (httpUrlConn != null) {
        httpUrlConn.disconnect();
      }
    }
  }
}
Пример #14
0
public class ReduceActor extends UntypedActor {
  LoggingAdapter log = Logging.getLogger(getContext().system(), this);
  private ActorRef actor = null;

  public ReduceActor(ActorRef inAggregateActor) {
    actor = inAggregateActor;
  }

  @Override
  public void onReceive(Object message) throws Exception {
    if (message instanceof List) {

      @SuppressWarnings("unchecked")
      List<Result> work = (List<Result>) message;

      // perform the work
      NavigableMap<String, Integer> reducedList = reduce(work);

      // reply with the result
      actor.tell(reducedList, self());
      // log.info("***********I'm in Reduce Actor on Receive, message: "
      // + message.toString() + "*******\n");
      // actor.tell("*********I'm in Reduicer Actor on Receive \n");

      Iterator i = reducedList.entrySet().iterator();
      while (i.hasNext()) {
        System.out.println("++++++ reduce done " + i.next());
      }

    } else throw new IllegalArgumentException("Unknown message [" + message + "]");
  }

  private NavigableMap<String, Integer> reduce(List<Result> list) {

    NavigableMap<String, Integer> reducedMap = new ConcurrentSkipListMap<String, Integer>();

    Iterator<Result> iter = list.iterator();
    while (iter.hasNext()) {
      Result result = iter.next();
      if (reducedMap.containsKey(result.getWord())) {
        Integer value = (Integer) reducedMap.get(result.getWord());
        value++;
        reducedMap.put(result.getWord(), value);
      } else {
        reducedMap.put(result.getWord(), Integer.valueOf(1));
      }
    }
    return reducedMap;
  }
}
public class WorkExecutor extends UntypedActor {

  private LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  @Override
  public void onReceive(Object message) {
    if (message instanceof Integer) {
      Integer n = (Integer) message;
      int n2 = n.intValue() * n.intValue();
      String result = n + " * " + n + " = " + n2;
      log.debug("Produced result {}", result);
      getSender().tell(new Worker.WorkComplete(result), getSelf());
    }
  }
}
Пример #16
0
public class Client extends UntypedActor {

  private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  private final UniqueNameGenerator nameGenerator = new UniqueNameGenerator();

  private final Config config;
  private final ActorRef app;

  public Client(Config config, ActorRef app) {
    this.config = config;
    this.app = app;
  }

  public static Props props(Config config, ActorRef app) {
    return Props.create(Client.class, config, app);
  }

  @Override
  public void onReceive(Object msg) throws Exception {
    if (msg instanceof Connect) {
      InetSocketAddress address = ((Connect) msg).getAddress();

      log.debug("connecting to " + address);

      ActorRef tcp = Tcp.get(getContext().system()).manager();
      tcp.tell(TcpMessage.connect(address), getSelf());
    } else if (msg instanceof CommandFailed) {
      log.error(msg.toString());
      app.tell(new ConnectFailed((CommandFailed) msg), getSelf());
    } else if (msg instanceof Connected) {
      log.info("connected");

      ActorRef listener =
          getContext()
              .actorOf(ClientListener.props(config), nameGenerator.getName(ClientListener.class));
      listener.tell(msg, getSender());

      getContext().watch(listener);
    } else if (msg instanceof Terminated) {
      log.warning("connection closed");
      app.tell(new ConnectionClosed(), getSelf());
    } else {
      unhandled(msg);
    }
  }
}
Пример #17
0
public class FactorialFrontend extends UntypedActor {
  final int upToN;
  final boolean repeat;

  LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  ActorRef backend =
      getContext().actorOf(FromConfig.getInstance().props(), "factorialBackendRouter");

  public FactorialFrontend(int upToN, boolean repeat) {
    this.upToN = upToN;
    this.repeat = repeat;
  }

  @Override
  public void preStart() {
    sendJobs();
    getContext().setReceiveTimeout(Duration.create(10, TimeUnit.SECONDS));
  }

  @Override
  public void onReceive(Object message) {
    if (message instanceof FactorialResult) {
      FactorialResult result = (FactorialResult) message;
      if (result.n == upToN) {
        log.debug("{}! = {}", result.n, result.factorial);
        if (repeat) sendJobs();
        else getContext().stop(getSelf());
      }

    } else if (message instanceof ReceiveTimeout) {
      log.info("Timeout");
      sendJobs();

    } else {
      unhandled(message);
    }
  }

  void sendJobs() {
    log.info("Starting batch of factorials up to [{}]", upToN);
    for (int n = 1; n <= upToN; n++) {
      backend.tell(n, getSelf());
    }
  }
}
Пример #18
0
public class MyUntypedActor extends UntypedActor {
  LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  public static void main(String[] args) {

    Props props1 = new Props();
    Props props2 = new Props(MyUntypedActor.class);
    Props props3 =
        new Props(
            new UntypedActorFactory() {
              public UntypedActor create() {
                return new MyUntypedActor();
              }
            });
    Props props4 =
        props1.withCreator(
            new UntypedActorFactory() {
              public UntypedActor create() {
                return new MyUntypedActor();
              }
            });

    ActorSystem system = ActorSystem.create("MyActorSystem", ConfigFactory.load());
    System.out.println(system.settings());
    system.actorOf(props2.withDispatcher("my-dispatcher"), "myactor");
  }

  @Override
  public void onReceive(Object message) throws Exception {
    if (message instanceof String) {
      log.info("Receive message string = " + message);
      getContext()
          .become(
              new Procedure<Object>() {

                @Override
                public void apply(Object arg0) {
                  // TODO Auto-generated method stub

                }
              });
    } else {
      unhandled(message);
    }
  }
}
Пример #19
0
  // #mdc-actor
  class MdcActor extends UntypedActor {

    final DiagnosticLoggingAdapter log = Logging.getLogger(this);

    public void onReceive(Object message) {

      Map<String, Object> mdc;
      mdc = new HashMap<String, Object>();
      mdc.put("requestId", 1234);
      mdc.put("visitorId", 5678);
      log.setMDC(mdc);

      log.info("Starting new request");

      log.clearMDC();
    }
  }
Пример #20
0
@Component
@Scope("prototype")
public class TaskActor extends UntypedActor {

  private final LoggingAdapter log = Logging.getLogger(getContext().system(), "TaskProcessor");

  @Autowired private TaskDAO taskDAO;

  @Autowired private AutowireTest atest;

  @Override
  public void onReceive(Object message) throws Exception {

    // atest.test();
    Long result = taskDAO.createTask((Task) message);
    log.debug("Created task {}", result);
  }
}
Пример #21
0
public class PullActor2 extends UntypedActor {

  ActorRef pullSocket =
      ZeroMQExtension.get(getContext().system())
          .newPullSocket(
              new SocketOption[] {new Connect("tcp://127.0.0.1:1237"), new Listener(getSelf())});
  LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  @Override
  public void onReceive(Object message) throws Exception {

    if (message instanceof ZMQMessage) {
      ZMQMessage m = (ZMQMessage) message;
      String mesg = new String(m.payload(0));

      log.info("Received Message ->" + mesg);
    }
  }
}
Пример #22
0
  public static class Swapper extends UntypedActor {
    LoggingAdapter log = Logging.getLogger(getContext().system(), this);

    public void onReceive(Object message) {
      if (message == SWAP) {
        log.info("Hi");
        getContext()
            .become(
                new Procedure<Object>() {
                  @Override
                  public void apply(Object message) {
                    log.info("Ho");
                    getContext().unbecome(); // resets the latest 'become'
                  }
                },
                false); // this signals stacking of the new behavior
      } else {
        unhandled(message);
      }
    }
  }
Пример #23
0
/** Created by jinggg on 16/3/27. */
public class MyWorker extends UntypedActor {

  private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  public static enum Msg {
    WORKING,
    DONE,
    CLOSE;
  }

  /** 可用来初始化资源 */
  @Override
  public void preStart() {
    System.out.println("MyWorker is staring");
  }

  /** 可用来释放资源 */
  @Override
  public void postStop() {
    System.out.println("MyWork is stopping");
  }

  @Override
  public void onReceive(Object message) throws Exception {
    if (message == Msg.WORKING) {
      System.out.println("I am working");
    }
    if (message == Msg.DONE) {
      System.out.println("Stop working");
    }
    if (message == Msg.CLOSE) {
      System.out.println("I will shutdown");
      getSender().tell(Msg.CLOSE, getSelf());
      getContext().stop(getSelf());
    } else {
      unhandled(message);
    }
  }
}
Пример #24
0
/**
 * @author Oleksandr Podstrelov
 * @version 1.0
 * @since 15/15/2015.
 */
public class IDCollector extends UntypedActor {

  private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
  private final String dataSeparator;
  private final String filePath;

  public IDCollector(String filePath, String dataSeparator) {
    this.filePath = filePath;
    this.dataSeparator = dataSeparator;
  }

  @Override
  public void onReceive(Object message) throws Exception {
    if (message == MessageEnum.COLLECT_ID) {
      log.info("Collecting unique ids from file...");
      final Set<Integer> ids = collectIdsFromFile(filePath);
      log.info("Collecting is finished");
      getSender().tell(ids, getSelf());
    } else {
      unhandled(message);
    }
  }

  private Set<Integer> collectIdsFromFile(String fileName) throws IOException {
    final Set<Integer> collectedIdSet = new HashSet<>();
    try (BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName))) {
      String line = null;
      while ((line = bufferedReader.readLine()) != null) {
        String[] splittedRow = line.split(dataSeparator);
        if (splittedRow.length > 0) {
          collectedIdSet.add(Integer.valueOf(splittedRow[0]));
        }
      }
    }
    return collectedIdSet;
  }
}
Пример #25
0
public class DoublingActor extends UntypedActor {

  LoggingAdapter log = Logging.getLogger(getContext().system(), this);

  public static Props mkProps() {
    return Props.create(DoublingActor.class);
  }

  @Override
  public void preStart() {
    log.debug("starting");
  }

  @Override
  public void onReceive(Object message) throws Exception {

    if (message instanceof Integer) {
      log.debug("received message: " + (Integer) message);
      getSender().tell((Integer) message * 2, getSelf());
    } else {
      unhandled(message);
    }
  }
}
/** Created by harshitha.suresh on 30/06/2015. */
public class PaintRedActor extends AbstractActor {
  private AtomicLong count = new AtomicLong();
  private final LoggingAdapter LOG = Logging.getLogger(context().system(), this);

  public PaintRedActor() {
    receive(
        ReceiveBuilder.match(Car.class, this::paint)
            .match(CountRequest.class, this::sendCount)
            .build());
  }

  private void sendCount(CountRequest countRequest) {
    getContext().sender().tell(new CountResponse(count.longValue()), getContext().self());
  }

  private void paint(Car car) {
    LOG.info("Paint car");
    car.setColour(Colour.RED);
    count.incrementAndGet();
    final ActorRef carMerger = getContext().actorFor("akka://carfactory/user/carMerger");
    final Inbox inbox = Inbox.create(getContext().system());
    inbox.send(carMerger, car);
  }
}
Пример #27
0
public class TaskStorage extends AbstractActor {
  public static final String TOPIC = "statusMessages";
  private LoggingAdapter log = Logging.getLogger(context().system(), this);

  private ActorRef clusterProxy;

  private final AtomicLong id = new AtomicLong();
  private final HashMap<Long, Status> statuses = new HashMap<>();
  private final HashMap<Long, String> tasks = new HashMap<>();
  private final Map<Long, SseBroadcaster> subscriptions = new HashMap<>();

  public static Props createProps() {
    return Props.create(TaskStorage.class);
  }

  @Override
  public void preStart() throws Exception {
    log.info("Starting TaskStorage");
    clusterProxy = context().system().actorFor(ActorSystemHolder.CLUSTER_PROXY_PATH);
    ActorRef mediator = DistributedPubSub.get(getContext().system()).mediator();
    mediator.tell(new DistributedPubSubMediator.Subscribe(TOPIC, self()), self());
  }

  @Override
  public PartialFunction<Object, BoxedUnit> receive() {
    return ReceiveBuilder.match(NewNumberCalculationMessage.class, this::log, this::queueNewMessage)
        .match(RegisterForNotifications.class, this::log, this::registerNewSSESubscription)
        .match(
            Worker.CalculationStarted.class,
            this::log,
            m -> recordStateChange(m.getTaskId(), Status.STARTED))
        .match(
            Worker.CalculationFinished.class,
            this::log,
            m -> recordStateChange(m.getTaskId(), Status.FINISHED))
        .match(GetTaskStatusMessage.class, this::log, this::handleGet)
        .match(DistributedPubSubMediator.SubscribeAck.class, m -> logSubscribeAck())
        .matchAny(this::unhandled)
        .build();
  }

  private boolean log(Object message) {
    log.info("Received message {}", message);
    return true;
  }

  private void handleGet(GetTaskStatusMessage message) {
    long taskId = message.getTaskId();

    TaskStatusMessage taskStatus =
        Optional.ofNullable(statuses.get(taskId))
            .map(st -> new TaskStatusMessage(taskId, tasks.get(taskId), st))
            .orElse(TaskStatusMessage.NOT_FOUND);
    sender().tell(taskStatus, self());
  }

  private void recordStateChange(long taskId, Status status) {
    statuses.put(taskId, status);
    broadcast(taskId, status);
  }

  private void queueNewMessage(NewNumberCalculationMessage message) {
    long taskId = id.incrementAndGet();
    tasks.put(taskId, message.getNumber());
    statuses.put(taskId, Status.QUEUED);
    TaskIdAssignedMessage taskIdAssignedMessage =
        new TaskIdAssignedMessage(taskId, message.getNumber());
    clusterProxy.tell(taskIdAssignedMessage, self());
    sender().tell(taskIdAssignedMessage, noSender());
  }

  private void registerNewSSESubscription(RegisterForNotifications message) {
    subscriptions.put(message.taskId, message.broadcaster);
    Status existingStatus = statuses.get(message.taskId);
    if (!QUEUED.equals(existingStatus)) {
      broadcast(message.taskId, existingStatus);
    }
  }

  private void broadcast(long taskId, TaskStorage.Status status) {
    OutboundEvent event =
        new OutboundEvent.Builder()
            .id(String.valueOf(taskId))
            .name(status.toString())
            .data(TaskStatusResource.class, new TaskStatusResource(taskId, null, status))
            .mediaType(MediaType.APPLICATION_JSON_TYPE)
            .build();
    Optional.ofNullable(subscriptions.get(taskId)).ifPresent(b -> b.broadcast(event));
  }

  private void logSubscribeAck() {
    log.info("Successfully subscribed for topic '{}'", TOPIC);
  }

  HashMap<Long, Status> getStatuses() {
    return statuses;
  }

  HashMap<Long, String> getTasks() {
    return tasks;
  }

  public enum Status {
    QUEUED,
    STARTED,
    FINISHED
  }

  public static class TaskIdAssignedMessage implements Serializable {
    private static final long serialVersionUID = 1L;

    private final long taskId;
    private final String number;

    public TaskIdAssignedMessage(long taskId, String number) {
      this.taskId = taskId;
      this.number = number;
    }

    public long getTaskId() {
      return taskId;
    }

    public String getNumber() {
      return number;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      TaskIdAssignedMessage that = (TaskIdAssignedMessage) o;
      return taskId == that.taskId && Objects.equals(number, that.number);
    }

    @Override
    public int hashCode() {
      return Objects.hash(taskId, number);
    }

    @Override
    public String toString() {
      return "TaskIdAssignedMessage{" + "taskId=" + taskId + ", number='" + number + '\'' + '}';
    }
  }

  public static class GetTaskStatusMessage implements Serializable {
    private static final long serialVersionUID = 1L;

    private final long taskId;

    public GetTaskStatusMessage(long taskId) {
      this.taskId = taskId;
    }

    public long getTaskId() {
      return taskId;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      GetTaskStatusMessage that = (GetTaskStatusMessage) o;
      return taskId == that.taskId;
    }

    @Override
    public int hashCode() {
      return Objects.hash(taskId);
    }

    @Override
    public String toString() {
      return "GetTaskStatusMessage{" + "taskId=" + taskId + '}';
    }
  }

  public static class TaskStatusMessage implements Serializable {
    private static final long serialVersionUID = 1L;
    public static final TaskStatusMessage NOT_FOUND =
        new TaskStatusMessage(Long.MIN_VALUE, "", null);

    private final long taskId;
    private final String number;
    private final Status status;

    public TaskStatusMessage(long taskId, String number, Status status) {
      this.taskId = taskId;
      this.number = number;
      this.status = status;
    }

    public long getTaskId() {
      return taskId;
    }

    public String getNumber() {
      return number;
    }

    public Status getStatus() {
      return status;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      TaskStatusMessage that = (TaskStatusMessage) o;
      return taskId == that.taskId && Objects.equals(number, that.number) && status == that.status;
    }

    @Override
    public int hashCode() {
      return Objects.hash(taskId, number, status);
    }

    @Override
    public String toString() {
      return "TaskStatusMessage{"
          + "taskId="
          + taskId
          + ", number='"
          + number
          + '\''
          + ", status="
          + status
          + '}';
    }
  }

  public static class RegisterForNotifications implements Serializable {
    private static final long serialVersionUID = 1L;

    private final long taskId;
    private final SseBroadcaster broadcaster;

    public RegisterForNotifications(long taskId, SseBroadcaster broadcaster) {
      this.taskId = taskId;
      this.broadcaster = broadcaster;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      RegisterForNotifications that = (RegisterForNotifications) o;
      return taskId == that.taskId && Objects.equals(broadcaster, that.broadcaster);
    }

    @Override
    public int hashCode() {
      return Objects.hash(taskId, broadcaster);
    }

    @Override
    public String toString() {
      return "RegisterForNotifications{"
          + "taskId="
          + taskId
          + ", broadcaster="
          + broadcaster
          + '}';
    }
  }
}
Пример #28
0
/** @author <a href="mailto:[email protected]">gvagenas</a> */
public class UssdInterpreter extends UntypedActor {

  // Logger.
  private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);

  static final int ERROR_NOTIFICATION = 0;
  static final int WARNING_NOTIFICATION = 1;
  static final Pattern PATTERN = Pattern.compile("[\\*#0-9]{1,12}");
  static String EMAIL_SENDER;

  // States for the FSM.
  // ==========================
  final State uninitialized;
  final State observeCall;
  final State acquiringCallInfo;
  final State disconnecting;
  final State cancelling;
  final State finished;

  private final State preparingMessage;
  private final State processingInfoRequest;

  // FSM.
  FiniteStateMachine fsm = null;
  // Information to reach the application that will be executed
  // by this interpreter.
  Sid accountId;
  Sid phoneId;
  String version;

  URI statusCallback;
  String statusCallbackMethod;
  String emailAddress;
  ActorRef ussdCall = null;
  CallInfo callInfo = null;
  // State for outbound calls.
  ActorRef outboundCall = null;
  CallInfo outboundCallInfo = null;

  // The call state.
  CallStateChanged.State callState = null;
  // A call detail record.
  CallDetailRecord callRecord = null;

  ActorRef downloader = null;
  // application data.
  HttpRequestDescriptor request;
  HttpResponseDescriptor response;
  // The RCML parser.
  ActorRef parser;
  Tag verb;
  DaoManager storage = null;
  final Set<Transition> transitions = new HashSet<Transition>();
  ActorRef mailerNotify = null;
  URI url;
  String method;
  URI fallbackUrl;
  String fallbackMethod;

  Tag ussdLanguageTag = null;
  int maxMessageLength;
  private final int englishLength = 182;
  private final int nonEnglishLength = 80;
  Queue<Tag> ussdMessageTags = new LinkedBlockingQueue<Tag>();
  Tag ussdCollectTag = null;
  String ussdCollectAction = "";

  Configuration configuration = null;

  private final State downloadingRcml;
  private final State downloadingFallbackRcml;
  private final State ready;
  private final State notFound;

  public UssdInterpreter(
      final Configuration configuration,
      final Sid account,
      final Sid phone,
      final String version,
      final URI url,
      final String method,
      final URI fallbackUrl,
      final String fallbackMethod,
      final URI statusCallback,
      final String statusCallbackMethod,
      final String emailAddress,
      final ActorRef callManager,
      final ActorRef conferenceManager,
      final ActorRef sms,
      final DaoManager storage) {
    super();
    final ActorRef source = self();

    uninitialized = new State("uninitialized", null, null);
    observeCall = new State("observe call", new ObserveCall(source), null);
    acquiringCallInfo = new State("acquiring call info", new AcquiringCallInfo(source), null);
    downloadingRcml = new State("downloading rcml", new DownloadingRcml(source), null);
    downloadingFallbackRcml =
        new State("downloading fallback rcml", new DownloadingFallbackRcml(source), null);
    preparingMessage = new State("Preparing message", new PreparingMessage(source), null);
    processingInfoRequest =
        new State("Processing info request from client", new ProcessingInfoRequest(source), null);

    ready = new State("ready", new Ready(source), null);
    notFound = new State("notFound", new NotFound(source), null);

    cancelling = new State("Cancelling", new Cancelling(source), null);
    disconnecting = new State("Disconnecting", new Disconnecting(source), null);

    finished = new State("finished", new Finished(source), null);

    transitions.add(new Transition(uninitialized, acquiringCallInfo));
    transitions.add(new Transition(uninitialized, cancelling));
    transitions.add(new Transition(acquiringCallInfo, downloadingRcml));
    transitions.add(new Transition(acquiringCallInfo, cancelling));
    transitions.add(new Transition(downloadingRcml, ready));
    transitions.add(new Transition(downloadingRcml, cancelling));
    transitions.add(new Transition(downloadingRcml, notFound));
    transitions.add(new Transition(downloadingRcml, downloadingFallbackRcml));
    transitions.add(new Transition(downloadingRcml, finished));
    transitions.add(new Transition(downloadingRcml, ready));
    transitions.add(new Transition(ready, preparingMessage));
    transitions.add(new Transition(preparingMessage, downloadingRcml));
    transitions.add(new Transition(preparingMessage, processingInfoRequest));
    transitions.add(new Transition(preparingMessage, disconnecting));
    transitions.add(new Transition(preparingMessage, finished));
    transitions.add(new Transition(processingInfoRequest, preparingMessage));
    transitions.add(new Transition(processingInfoRequest, ready));
    transitions.add(new Transition(processingInfoRequest, finished));
    transitions.add(new Transition(disconnecting, finished));

    // Initialize the FSM.
    this.fsm = new FiniteStateMachine(uninitialized, transitions);
    // Initialize the runtime stuff.
    this.accountId = account;
    this.phoneId = phone;
    this.version = version;
    this.url = url;
    this.method = method;
    this.fallbackUrl = fallbackUrl;
    this.fallbackMethod = fallbackMethod;
    this.statusCallback = statusCallback;
    this.statusCallbackMethod = statusCallbackMethod;
    this.emailAddress = emailAddress;
    this.configuration = configuration;

    this.storage = storage;
    final Configuration runtime = configuration.subset("runtime-settings");
    String path = runtime.getString("cache-path");
    if (!path.endsWith("/")) {
      path = path + "/";
    }
    path = path + accountId.toString();
    this.downloader = downloader();
  }

  private Notification notification(final int log, final int error, final String message) {
    final Notification.Builder builder = Notification.builder();
    final Sid sid = Sid.generate(Sid.Type.NOTIFICATION);
    builder.setSid(sid);
    builder.setAccountSid(accountId);
    builder.setCallSid(callInfo.sid());
    builder.setApiVersion(version);
    builder.setLog(log);
    builder.setErrorCode(error);
    final String base = configuration.subset("runtime-settings").getString("error-dictionary-uri");
    StringBuilder buffer = new StringBuilder();
    buffer.append(base);
    if (!base.endsWith("/")) {
      buffer.append("/");
    }
    buffer.append(error).append(".html");
    final URI info = URI.create(buffer.toString());
    builder.setMoreInfo(info);
    builder.setMessageText(message);
    final DateTime now = DateTime.now();
    builder.setMessageDate(now);
    if (request != null) {
      builder.setRequestUrl(request.getUri());
      builder.setRequestMethod(request.getMethod());
      builder.setRequestVariables(request.getParametersAsString());
    }
    if (response != null) {
      builder.setResponseHeaders(response.getHeadersAsString());
      final String type = response.getContentType();
      if (type.contains("text/xml")
          || type.contains("application/xml")
          || type.contains("text/html")) {
        try {
          builder.setResponseBody(response.getContentAsString());
        } catch (final IOException exception) {
          logger.error(
              "There was an error while reading the contents of the resource "
                  + "located @ "
                  + url.toString(),
              exception);
        }
      }
    }
    buffer = new StringBuilder();
    buffer.append("/").append(version).append("/Accounts/");
    buffer.append(accountId.toString()).append("/Notifications/");
    buffer.append(sid.toString());
    final URI uri = URI.create(buffer.toString());
    builder.setUri(uri);
    return builder.build();
  }

  ActorRef mailer(final Configuration configuration) {
    final UntypedActorContext context = getContext();
    return context.actorOf(
        new Props(
            new UntypedActorFactory() {
              private static final long serialVersionUID = 1L;

              @Override
              public Actor create() throws Exception {
                return new EmailService(configuration);
              }
            }));
  }

  ActorRef downloader() {
    final UntypedActorContext context = getContext();
    return context.actorOf(
        new Props(
            new UntypedActorFactory() {
              private static final long serialVersionUID = 1L;

              @Override
              public UntypedActor create() throws Exception {
                return new Downloader();
              }
            }));
  }

  ActorRef parser(final String xml) {
    final UntypedActorContext context = getContext();
    return context.actorOf(
        new Props(
            new UntypedActorFactory() {
              private static final long serialVersionUID = 1L;

              @Override
              public UntypedActor create() throws Exception {
                return new Parser(xml, self());
              }
            }));
  }

  void invalidVerb(final Tag verb) {
    final ActorRef self = self();
    // Get the next verb.
    final GetNextVerb next = GetNextVerb.instance();
    parser.tell(next, self);
  }

  List<NameValuePair> parameters() {
    final List<NameValuePair> parameters = new ArrayList<NameValuePair>();
    final String callSid = callInfo.sid().toString();
    parameters.add(new BasicNameValuePair("CallSid", callSid));
    final String accountSid = accountId.toString();
    parameters.add(new BasicNameValuePair("AccountSid", accountSid));
    final String from = (callInfo.from());
    parameters.add(new BasicNameValuePair("From", from));
    final String to = (callInfo.to());
    parameters.add(new BasicNameValuePair("To", to));
    final String state = callState.toString();
    parameters.add(new BasicNameValuePair("CallStatus", state));
    parameters.add(new BasicNameValuePair("ApiVersion", version));
    final String direction = callInfo.direction();
    parameters.add(new BasicNameValuePair("Direction", direction));
    final String callerName = callInfo.fromName();
    parameters.add(new BasicNameValuePair("CallerName", callerName));
    final String forwardedFrom = callInfo.forwardedFrom();
    parameters.add(new BasicNameValuePair("ForwardedFrom", forwardedFrom));
    // logger.info("Type " + callInfo.type());
    if (CreateCall.Type.SIP == callInfo.type()) {
      // Adding SIP OUT Headers and SipCallId for
      // https://bitbucket.org/telestax/telscale-restcomm/issue/132/implement-twilio-sip-out
      SipServletResponse lastResponse = callInfo.lastResponse();
      // logger.info("lastResponse " + lastResponse);
      if (lastResponse != null) {
        final int statusCode = lastResponse.getStatus();
        final String method = lastResponse.getMethod();
        // See https://www.twilio.com/docs/sip/receiving-sip-headers
        // Headers on the final SIP response message (any 4xx or 5xx message or the final BYE/200)
        // are posted to the
        // Dial action URL.
        if ((statusCode >= 400 && "INVITE".equalsIgnoreCase(method))
            || (statusCode >= 200 && statusCode < 300 && "BYE".equalsIgnoreCase(method))) {
          final String sipCallId = lastResponse.getCallId();
          parameters.add(new BasicNameValuePair("DialSipCallId", sipCallId));
          parameters.add(new BasicNameValuePair("DialSipResponseCode", "" + statusCode));
          Iterator<String> headerIt = lastResponse.getHeaderNames();
          while (headerIt.hasNext()) {
            String headerName = headerIt.next();
            if (headerName.startsWith("X-")) {
              parameters.add(
                  new BasicNameValuePair(
                      "DialSipHeader_" + headerName, lastResponse.getHeader(headerName)));
            }
          }
        }
      }
    }
    return parameters;
  }

  void sendMail(final Notification notification) {
    if (emailAddress == null || emailAddress.isEmpty()) {
      return;
    }
    final String EMAIL_SUBJECT = "RestComm Error Notification - Attention Required";
    final StringBuilder buffer = new StringBuilder();
    buffer.append("<strong>").append("Sid: ").append("</strong></br>");
    buffer.append(notification.getSid().toString()).append("</br>");
    buffer.append("<strong>").append("Account Sid: ").append("</strong></br>");
    buffer.append(notification.getAccountSid().toString()).append("</br>");
    buffer.append("<strong>").append("Call Sid: ").append("</strong></br>");
    buffer.append(notification.getCallSid().toString()).append("</br>");
    buffer.append("<strong>").append("API Version: ").append("</strong></br>");
    buffer.append(notification.getApiVersion()).append("</br>");
    buffer.append("<strong>").append("Log: ").append("</strong></br>");
    buffer
        .append(notification.getLog() == ERROR_NOTIFICATION ? "ERROR" : "WARNING")
        .append("</br>");
    buffer.append("<strong>").append("Error Code: ").append("</strong></br>");
    buffer.append(notification.getErrorCode()).append("</br>");
    buffer.append("<strong>").append("More Information: ").append("</strong></br>");
    buffer.append(notification.getMoreInfo().toString()).append("</br>");
    buffer.append("<strong>").append("Message Text: ").append("</strong></br>");
    buffer.append(notification.getMessageText()).append("</br>");
    buffer.append("<strong>").append("Message Date: ").append("</strong></br>");
    buffer.append(notification.getMessageDate().toString()).append("</br>");
    buffer.append("<strong>").append("Request URL: ").append("</strong></br>");
    buffer.append(notification.getRequestUrl().toString()).append("</br>");
    buffer.append("<strong>").append("Request Method: ").append("</strong></br>");
    buffer.append(notification.getRequestMethod()).append("</br>");
    buffer.append("<strong>").append("Request Variables: ").append("</strong></br>");
    buffer.append(notification.getRequestVariables()).append("</br>");
    buffer.append("<strong>").append("Response Headers: ").append("</strong></br>");
    buffer.append(notification.getResponseHeaders()).append("</br>");
    buffer.append("<strong>").append("Response Body: ").append("</strong></br>");
    buffer.append(notification.getResponseBody()).append("</br>");
    final Mail emailMsg = new Mail(EMAIL_SENDER, emailAddress, EMAIL_SUBJECT, buffer.toString());
    if (mailerNotify == null) {
      mailerNotify = mailer(configuration.subset("smtp-notify"));
    }
    mailerNotify.tell(new EmailRequest(emailMsg), self());
  }

  void callback() {
    if (statusCallback != null) {
      if (statusCallbackMethod == null) {
        statusCallbackMethod = "POST";
      }
      final List<NameValuePair> parameters = parameters();
      request = new HttpRequestDescriptor(statusCallback, statusCallbackMethod, parameters);
      downloader.tell(request, null);
    }
  }

  @Override
  public void onReceive(final Object message) throws Exception {
    final Class<?> klass = message.getClass();
    final State state = fsm.state();
    final ActorRef sender = sender();
    final ActorRef source = self();

    if (logger.isInfoEnabled()) {
      logger.info(" ********** UssdInterpreter's Current State: " + state.toString());
      logger.info(" ********** UssdInterpreter's Processing Message: " + klass.getName());
    }

    if (StartInterpreter.class.equals(klass)) {
      ussdCall = ((StartInterpreter) message).resource();
      fsm.transition(message, acquiringCallInfo);
    } else if (message instanceof SipServletRequest) {
      SipServletRequest request = (SipServletRequest) message;
      String method = request.getMethod();
      if ("INFO".equalsIgnoreCase(method)) {
        fsm.transition(message, processingInfoRequest);
      } else if ("ACK".equalsIgnoreCase(method)) {
        fsm.transition(message, downloadingRcml);
      } else if ("BYE".equalsIgnoreCase(method)) {
        fsm.transition(message, disconnecting);
      } else if ("CANCEL".equalsIgnoreCase(method)) {
        fsm.transition(message, cancelling);
      }
    } else if (CallStateChanged.class.equals(klass)) {
      final CallStateChanged event = (CallStateChanged) message;
      callState = event.state();
      if (CallStateChanged.State.RINGING == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info("CallStateChanged.State.RINGING");
        }
      } else if (CallStateChanged.State.IN_PROGRESS == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info("CallStateChanged.State.IN_PROGRESS");
        }
      } else if (CallStateChanged.State.NO_ANSWER == event.state()
          || CallStateChanged.State.COMPLETED == event.state()
          || CallStateChanged.State.FAILED == event.state()
          || CallStateChanged.State.CANCELED == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info(
              "CallStateChanged.State.NO_ANSWER OR  CallStateChanged.State.COMPLETED OR CallStateChanged.State.FAILED or CallStateChanged.State.CANCELED");
        }
        fsm.transition(message, finished);
      } else if (CallStateChanged.State.BUSY == event.state()) {
        if (logger.isInfoEnabled()) {
          logger.info("CallStateChanged.State.BUSY");
        }
      }
      //            else if (CallStateChanged.State.COMPLETED == event.state()) {
      //                logger.info("CallStateChanged.State.Completed");
      //                fsm.transition(message, finished);
      //            }
    } else if (CallResponse.class.equals(klass)) {
      if (acquiringCallInfo.equals(state)) {
        @SuppressWarnings("unchecked")
        final CallResponse<CallInfo> response = (CallResponse<CallInfo>) message;
        // Check from whom is the message (initial call or outbound call) and update info
        // accordingly
        if (sender == ussdCall) {
          callInfo = response.get();
        } else {
          outboundCallInfo = response.get();
        }
        final String direction = callInfo.direction();
        if ("inbound".equals(direction)) {
          ussdCall.tell(new Answer(callInfo.sid()), source);
          // fsm.transition(message, downloadingRcml);
        } else {
          fsm.transition(message, downloadingRcml);
          //                     fsm.transition(message, initializingCall);
        }
      }
    } else if (DownloaderResponse.class.equals(klass)) {
      final DownloaderResponse response = (DownloaderResponse) message;
      if (response.succeeded() && HttpStatus.SC_OK == response.get().getStatusCode()) {
        if (logger.isDebugEnabled()) {
          logger.debug(
              "Rcml URI : "
                  + response.get().getURI()
                  + "response succeeded "
                  + response.succeeded()
                  + ", statusCode "
                  + response.get().getStatusCode());
        }
        fsm.transition(message, ready);
      } else if (response.succeeded()
          && HttpStatus.SC_NOT_FOUND == response.get().getStatusCode()) {
        fsm.transition(message, notFound);
      } else {
        if (downloadingRcml.equals(state)) {
          if (fallbackUrl != null) {
            fsm.transition(message, downloadingFallbackRcml);
          } else {
            fsm.transition(message, finished);
          }
        } else {
          fsm.transition(message, finished);
        }
      }
    } else if (ParserFailed.class.equals(klass)) {
      if (logger.isInfoEnabled()) {
        logger.info("ParserFailed received. Will stop the call");
      }
      fsm.transition(message, cancelling);
    } else if (Tag.class.equals(klass)) {
      final Tag verb = (Tag) message;
      if (ussdLanguage.equals(verb.name())) {
        if (ussdLanguageTag == null) {
          ussdLanguageTag = verb;
          final GetNextVerb next = GetNextVerb.instance();
          parser.tell(next, source);
        } else {
          // We support only one Language element
          invalidVerb(verb);
        }
        return;
      } else if (ussdMessage.equals(verb.name())) {
        ussdMessageTags.add(verb);
        final GetNextVerb next = GetNextVerb.instance();
        parser.tell(next, source);
        return;
      } else if (ussdCollect.equals(verb.name())) {
        if (ussdCollectTag == null) {
          ussdCollectTag = verb;
          final GetNextVerb next = GetNextVerb.instance();
          parser.tell(next, source);
        } else {
          // We support only one Collect element
          invalidVerb(verb);
        }
        return;
      } else {
        invalidVerb(verb);
      }
    } else if (End.class.equals(klass)) {
      fsm.transition(message, preparingMessage);
    }
  }

  abstract class AbstractAction implements Action {
    protected final ActorRef source;

    public AbstractAction(final ActorRef source) {
      super();
      this.source = source;
    }
  }

  final class ObserveCall extends AbstractAction {
    public ObserveCall(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      ussdCall.tell(new Observe(source), source);
    }
  }

  final class AcquiringCallInfo extends AbstractAction {
    public AcquiringCallInfo(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("Acquiring Call Info");
      }
      ussdCall.tell(new Observe(source), source);
      ussdCall.tell(new GetCallInfo(), source);
    }
  }

  private final class DownloadingRcml extends AbstractAction {
    public DownloadingRcml(final ActorRef source) {
      super(source);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("Downloading RCML");
      }
      final Class<?> klass = message.getClass();
      final CallDetailRecordsDao records = storage.getCallDetailRecordsDao();
      if (CallResponse.class.equals(klass)) {
        final CallResponse<CallInfo> response = (CallResponse<CallInfo>) message;
        callInfo = response.get();
        callState = callInfo.state();
        if (callInfo.direction().equals("inbound")) {
          // Create a call detail record for the call.
          final CallDetailRecord.Builder builder = CallDetailRecord.builder();
          builder.setSid(callInfo.sid());
          builder.setInstanceId(RestcommConfiguration.getInstance().getMain().getInstanceId());
          builder.setDateCreated(callInfo.dateCreated());
          builder.setAccountSid(accountId);
          builder.setTo(callInfo.to());
          builder.setCallerName(callInfo.fromName());
          builder.setFrom(callInfo.from());
          builder.setForwardedFrom(callInfo.forwardedFrom());
          builder.setPhoneNumberSid(phoneId);
          builder.setStatus(callState.toString());
          final DateTime now = DateTime.now();
          builder.setStartTime(now);
          builder.setDirection(callInfo.direction());
          builder.setApiVersion(version);
          builder.setPrice(new BigDecimal("0.00"));
          // TODO implement currency property to be read from Configuration
          builder.setPriceUnit(Currency.getInstance("USD"));
          final StringBuilder buffer = new StringBuilder();
          buffer.append("/").append(version).append("/Accounts/");
          buffer.append(accountId.toString()).append("/Calls/");
          buffer.append(callInfo.sid().toString());
          final URI uri = URI.create(buffer.toString());
          builder.setUri(uri);

          builder.setCallPath(ussdCall.path().toString());

          callRecord = builder.build();
          records.addCallDetailRecord(callRecord);
          // Update the application.
          callback();
        }
      }
      // Ask the downloader to get us the application that will be executed.
      final List<NameValuePair> parameters = parameters();
      request = new HttpRequestDescriptor(url, method, parameters);
      downloader.tell(request, source);
    }
  }

  private final class DownloadingFallbackRcml extends AbstractAction {
    public DownloadingFallbackRcml(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("Downloading Fallback RCML");
      }
      final Class<?> klass = message.getClass();
      // Notify the account of the issue.
      if (DownloaderResponse.class.equals(klass)) {
        final DownloaderResponse result = (DownloaderResponse) message;
        final Throwable cause = result.cause();
        Notification notification = null;
        if (cause instanceof ClientProtocolException) {
          notification = notification(ERROR_NOTIFICATION, 11206, cause.getMessage());
        } else if (cause instanceof IOException) {
          notification = notification(ERROR_NOTIFICATION, 11205, cause.getMessage());
        } else if (cause instanceof URISyntaxException) {
          notification = notification(ERROR_NOTIFICATION, 11100, cause.getMessage());
        }
        if (notification != null) {
          final NotificationsDao notifications = storage.getNotificationsDao();
          notifications.addNotification(notification);
          sendMail(notification);
        }
      }
      // Try to use the fall back url and method.
      final List<NameValuePair> parameters = parameters();
      request = new HttpRequestDescriptor(fallbackUrl, fallbackMethod, parameters);
      downloader.tell(request, source);
    }
  }

  private final class Ready extends AbstractAction {
    public Ready(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("In Ready state");
      }
      // ussdCall.tell(new Answer(), source);
      // Execute the received RCML here
      final UntypedActorContext context = getContext();
      final State state = fsm.state();
      if (downloadingRcml.equals(state)
          || downloadingFallbackRcml.equals(state)
          || processingInfoRequest.equals(state)) {
        response = ((DownloaderResponse) message).get();
        if (parser != null) {
          context.stop(parser);
          parser = null;
        }
        final String type = response.getContentType();
        if (type.contains("text/xml")
            || type.contains("application/xml")
            || type.contains("text/html")) {
          parser = parser(response.getContentAsString());
        } else if (type.contains("text/plain")) {
          parser = parser("<UssdMessage>" + response.getContentAsString() + "</UssdMessage>");
        } else {
          final StopInterpreter stop = new StopInterpreter();
          source.tell(stop, source);
          return;
        }
      }
      final GetNextVerb next = GetNextVerb.instance();
      parser.tell(next, source);
    }
  }

  private final class NotFound extends AbstractAction {
    public NotFound(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("In Not Found State");
      }
      final DownloaderResponse response = (DownloaderResponse) message;
      if (logger.isDebugEnabled()) {
        logger.debug(
            "response succeeded "
                + response.succeeded()
                + ", statusCode "
                + response.get().getStatusCode());
      }
      final Notification notification =
          notification(WARNING_NOTIFICATION, 21402, "URL Not Found : " + response.get().getURI());
      final NotificationsDao notifications = storage.getNotificationsDao();
      notifications.addNotification(notification);
      // Hang up the call.
      ussdCall.tell(new org.restcomm.connect.telephony.api.NotFound(), source);
    }
  }

  // RCML END received, construct the USSD Message and ask UssdCall to send it
  private final class PreparingMessage extends AbstractAction {
    public PreparingMessage(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("Preparing the USSD Message");
      }
      if (End.class.equals(message.getClass())) {

        Boolean hasCollect = false;
        UssdRestcommResponse ussdRestcommResponse = new UssdRestcommResponse();

        String language = "";
        if (ussdLanguageTag == null) {
          language = "en";
          ussdRestcommResponse.setLanguage(language);
        } else {
          language = ussdLanguageTag.text();
          ussdRestcommResponse.setLanguage(language);
        }

        if (language.equalsIgnoreCase("en")) {
          maxMessageLength = englishLength;
          ussdRestcommResponse.setMessageLength(englishLength);
        } else {
          maxMessageLength = nonEnglishLength;
          ussdRestcommResponse.setMessageLength(nonEnglishLength);
        }

        StringBuffer ussdText = processUssdMessageTags(ussdMessageTags);

        if (ussdCollectTag != null) {
          hasCollect = true;
          ussdCollectAction = ussdCollectTag.attribute("action").value();
          ussdRestcommResponse.setUssdCollectAction(ussdCollectAction);
          Queue<Tag> children =
              new java.util.concurrent.ConcurrentLinkedQueue<Tag>(ussdCollectTag.children());
          if (children != null && children.size() > 0) {
            ussdText.append(processUssdMessageTags(children));
          } else if (ussdCollectTag.text() != null) {
            ussdText.append(ussdCollectTag.text());
          }
        }

        if (ussdText.length() > maxMessageLength) {
          final String errorString =
              "Error while preparing the USSD response. Ussd text length more "
                  + "than the permitted for the selected language: "
                  + maxMessageLength;
          Notification notification = notification(ERROR_NOTIFICATION, 11100, errorString);
          if (notification != null) {
            final NotificationsDao notifications = storage.getNotificationsDao();
            notifications.addNotification(notification);
            sendMail(notification);
          }
          if (logger.isInfoEnabled()) {
            logger.info(errorString);
          }
          ussdText = new StringBuffer();
          ussdText.append(
              "Error while preparing the response.\nMessage length exceeds the maximum.");
        }

        ussdRestcommResponse.setMessage(ussdText.toString());

        if (logger.isInfoEnabled()) {
          logger.info("UssdMessage prepared, hasCollect: " + hasCollect);
          logger.info(
              "UssdMessage prepared: " + ussdMessage.toString() + " hasCollect: " + hasCollect);
        }

        if (callInfo.direction().equalsIgnoreCase("inbound")) {
          // USSD PULL
          if (hasCollect) {
            ussdRestcommResponse.setMessageType(UssdMessageType.unstructuredSSRequest_Request);
            ussdRestcommResponse.setIsFinalMessage(false);
          } else {
            ussdRestcommResponse.setMessageType(
                UssdMessageType.processUnstructuredSSRequest_Response);
            ussdRestcommResponse.setIsFinalMessage(true);
          }
        } else {
          // USSD PUSH
          if (hasCollect) {
            ussdRestcommResponse.setMessageType(UssdMessageType.unstructuredSSRequest_Request);
            ussdRestcommResponse.setIsFinalMessage(false);
          } else {
            ussdRestcommResponse.setMessageType(UssdMessageType.unstructuredSSNotify_Request);
            if (ussdRestcommResponse.getErrorCode() != null) {
              ussdRestcommResponse.setIsFinalMessage(true);
            } else {
              ussdRestcommResponse.setIsFinalMessage(false);
            }
          }
        }
        if (logger.isInfoEnabled()) {
          logger.info("UssdRestcommResponse message prepared: " + ussdRestcommResponse);
        }
        ussdCall.tell(ussdRestcommResponse, source);
      }
    }
  }

  private StringBuffer processUssdMessageTags(Queue<Tag> messageTags) {
    StringBuffer message = new StringBuffer();
    while (!messageTags.isEmpty()) {
      Tag tag = messageTags.poll();
      if (tag != null) {
        message.append(tag.text());
        if (!messageTags.isEmpty()) message.append("\n");
      } else {
        return message;
      }
    }
    return message;
  }

  private final class ProcessingInfoRequest extends AbstractAction {
    public ProcessingInfoRequest(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("UssdInterpreter Processing INFO request");
      }
      final NotificationsDao notifications = storage.getNotificationsDao();
      SipServletRequest info = (SipServletRequest) message;

      SipServletResponse okay = info.createResponse(200);
      okay.send();

      UssdInfoRequest ussdInfoRequest = new UssdInfoRequest(info);
      String ussdText = ussdInfoRequest.getMessage();
      if (ussdCollectAction != null && !ussdCollectAction.isEmpty() && ussdText != null) {
        URI target = null;
        try {
          target = URI.create(ussdCollectAction);
        } catch (final Exception exception) {
          final Notification notification =
              notification(ERROR_NOTIFICATION, 11100, ussdCollectAction + " is an invalid URI.");
          notifications.addNotification(notification);
          sendMail(notification);
          final StopInterpreter stop = new StopInterpreter();
          source.tell(stop, source);
          return;
        }
        final URI base = request.getUri();
        final URI uri = UriUtils.resolve(base, target);
        // Parse "method".
        String method = "POST";
        Attribute attribute = null;
        try {
          attribute = verb.attribute("method");
        } catch (Exception e) {
        }
        if (attribute != null) {
          method = attribute.value();
          if (method != null && !method.isEmpty()) {
            if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method)) {
              final Notification notification =
                  notification(
                      WARNING_NOTIFICATION,
                      14104,
                      method + " is not a valid HTTP method for <Gather>");
              notifications.addNotification(notification);
              method = "POST";
            }
          } else {
            method = "POST";
          }
        }

        final List<NameValuePair> parameters = parameters();
        parameters.add(new BasicNameValuePair("Digits", ussdText));
        request = new HttpRequestDescriptor(uri, method, parameters);
        downloader.tell(request, source);
        ussdCollectTag = null;
        ussdLanguageTag = null;
        ussdMessageTags = new LinkedBlockingQueue<Tag>();
        return;
      } else if (ussdInfoRequest
          .getUssdMessageType()
          .equals(UssdMessageType.unstructuredSSNotify_Response)) {
        UssdRestcommResponse ussdRestcommResponse = new UssdRestcommResponse();
        ussdRestcommResponse.setErrorCode("1");
        ussdRestcommResponse.setIsFinalMessage(true);
        ussdCall.tell(ussdRestcommResponse, source);
        return;
      }
      // Ask the parser for the next action to take.
      final GetNextVerb next = GetNextVerb.instance();
      parser.tell(next, self());
    }
  }

  private final class Cancelling extends AbstractAction {
    public Cancelling(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("Cancelling state");
      }
      final Class<?> klass = message.getClass();
      if (message instanceof SipServletRequest) {
        SipServletRequest request = (SipServletRequest) message;
        if (ussdCall != null) ussdCall.tell(request, self());
        if (outboundCall != null) ussdCall.tell(request, self());
      }
    }
  }

  private final class Disconnecting extends AbstractAction {
    public Disconnecting(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("Disconnecting state");
      }
      final Class<?> klass = message.getClass();
      if (message instanceof SipServletRequest) {
        SipServletRequest request = (SipServletRequest) message;
        if (ussdCall != null) ussdCall.tell(request, self());
        if (outboundCall != null) ussdCall.tell(request, self());
      }
    }
  }

  private final class Finished extends AbstractAction {
    public Finished(final ActorRef source) {
      super(source);
    }

    @Override
    public void execute(final Object message) throws Exception {
      if (logger.isInfoEnabled()) {
        logger.info("In Finished state");
      }
      final Class<?> klass = message.getClass();
      if (CallStateChanged.class.equals(klass)) {
        final CallStateChanged event = (CallStateChanged) message;
        callState = event.state();
        if (callRecord != null) {
          callRecord = callRecord.setStatus(callState.toString());
          final DateTime end = DateTime.now();
          callRecord = callRecord.setEndTime(end);
          final int seconds =
              (int) (end.getMillis() - callRecord.getStartTime().getMillis()) / 1000;
          callRecord = callRecord.setDuration(seconds);
          final CallDetailRecordsDao records = storage.getCallDetailRecordsDao();
          records.updateCallDetailRecord(callRecord);
        }
        callback();
      }
      context().stop(self());
    }
  }

  @Override
  public void postStop() {
    if (logger.isInfoEnabled()) {
      logger.info("UssdInterpreter postStop");
    }
    if (ussdCall != null) getContext().stop(ussdCall);
    if (outboundCall != null) getContext().stop(outboundCall);
    if (downloader != null) getContext().stop(downloader);
    if (parser != null) getContext().stop(parser);
    if (mailerNotify != null) getContext().stop(mailerNotify);
    super.postStop();
  }
}
Пример #29
0
  public static void main(String[] args) throws Exception {

    ApplicationContext context = SpringApplication.run(App.class, args);

    ActorSystem system = context.getBean(ActorSystem.class);

    final LoggingAdapter log = Logging.getLogger(system, App.class.getSimpleName());

    log.info("Starting up");

    SpringExtension ext = context.getBean(SpringExtension.class);

    // Use the Spring Extension to create props for a named actor bean
    ActorRef calculator =
        system.actorOf(ext.props("calculator").withMailbox("akka.priority-mailbox"));

    /*
     * Create a completion service instance to await all futures
     */
    final ExecutionContext ec = system.dispatcher();

    Timeout timeout = new Timeout(120, TimeUnit.SECONDS);
    List<Long> ids = new ArrayList<>();

    ArrayList<Future<Object>> futures = new ArrayList<Future<Object>>();

    for (int i = 1; i <= 10; i++) {
      Future<Object> future = Patterns.ask(calculator, new CompilationRequest(i + " + 5"), timeout);
      future.onSuccess(
          new OnSuccess<Object>() {
            public void onSuccess(Object result) {
              if (result instanceof CompilationResult) {
                synchronized (ids) {
                  log.debug("Compilation result {} ", result.toString());
                  ids.add(((CompilationResult) result).getExpressionId());
                }
              } else {
                log.info("Compilation result is unknown type {} ", result.toString());
              }
            }
          },
          ec);
      futures.add(future);
    }

    Future<Iterable<Object>> seq = Futures.sequence(futures, ec);
    Await.result(seq, Duration.create(30, SECONDS));
    log.info("======================================================");
    log.info("Done waiting for compilations...{} ids", ids.size());
    log.info("======================================================");
    futures.clear();

    long start = System.nanoTime();
    List<Double> results = new ArrayList<>();

    Long count = 1_000_000L;
    for (long i = 1; i <= count; i++) {
      Future<Object> future =
          Patterns.ask(
              calculator,
              new CalculationRequest(i, ids.get((int) (i % ids.size())), null),
              timeout);
      future.onSuccess(
          new OnSuccess<Object>() {
            public void onSuccess(Object result) {
              if (result instanceof CalculationResult) {
                log.debug("Calculation result {} ", result.toString());
                //						synchronized(results)
                //						{
                //							results.add((Double) ((CalculationResult) result).getValue());
                //						}
              } else {
                log.info("Calculation result is unknown type {} ", result.toString());
              }
            }
          },
          ec);
      futures.add(future);
    }
    seq = Futures.sequence(futures, ec);
    Await.result(seq, Duration.create(600, SECONDS));
    calculator.tell(PoisonPill.getInstance(), null);

    while (!calculator.isTerminated()) {
      Thread.sleep(100);
    }

    long end = System.nanoTime();
    //        //int count = context.getBean(JdbcTemplate.class).queryForObject("SELECT COUNT(*) FROM
    // tasks", Integer.class);
    Long elapsed = TimeUnit.MILLISECONDS.convert((end - start), TimeUnit.NANOSECONDS);
    Double tps = count.doubleValue() / (elapsed.doubleValue() / Double.parseDouble("1000"));
    log.info("{} calculations in {}ms {}tps", count, elapsed, tps);
    log.info("Shutting down ------------------------------> {}", results.size());
    Thread.sleep(10000);
    system.shutdown();
    system.awaitTermination();
  }
Пример #30
0
public class Worker extends UntypedActor {

  public static Props props(
      ActorRef clusterClient, Props workExecutorProps, FiniteDuration registerInterval) {
    return Props.create(Worker.class, clusterClient, workExecutorProps, registerInterval);
  }

  public static Props props(ActorRef clusterClient, Props workExecutorProps) {
    return props(clusterClient, workExecutorProps, Duration.create(10, "seconds"));
  }

  private final ActorRef clusterClient;
  private LoggingAdapter log = Logging.getLogger(getContext().system(), this);
  private final String workerId = UUID.randomUUID().toString();
  private final ActorRef workExecutor;
  private final Cancellable registerTask;
  private String currentWorkId = null;

  public Worker(ActorRef clusterClient, Props workExecutorProps, FiniteDuration registerInterval) {
    this.clusterClient = clusterClient;
    this.workExecutor = getContext().watch(getContext().actorOf(workExecutorProps, "exec"));
    this.registerTask =
        getContext()
            .system()
            .scheduler()
            .schedule(
                Duration.Zero(),
                registerInterval,
                clusterClient,
                new SendToAll("/user/master/singleton", new RegisterWorker(workerId)),
                getContext().dispatcher(),
                getSelf());
  }

  private String workId() {
    if (currentWorkId != null) return currentWorkId;
    else throw new IllegalStateException("Not working");
  }

  @Override
  public SupervisorStrategy supervisorStrategy() {
    return new OneForOneStrategy(
        -1,
        Duration.Inf(),
        new Function<Throwable, Directive>() {

          // @Override
          public Directive apply(Throwable t) {
            if (t instanceof ActorInitializationException) return stop();
            else if (t instanceof DeathPactException) return stop();
            else if (t instanceof Exception) {
              if (currentWorkId != null) sendToMaster(new WorkFailed(workerId, workId()));
              getContext().become(idle);
              return restart();
            } else {
              return escalate();
            }
          }
        });
  }

  @Override
  public void postStop() {
    registerTask.cancel();
  }

  public void onReceive(Object message) {
    unhandled(message);
  }

  private final Procedure<Object> idle =
      new Procedure<Object>() {
        public void apply(Object message) {
          if (message instanceof MasterWorkerProtocol.WorkIsReady)
            sendToMaster(new MasterWorkerProtocol.WorkerRequestsWork(workerId));
          else if (message instanceof Work) {
            Work work = (Work) message;
            log.info("Got work: {}", work.job);
            currentWorkId = work.workId;
            workExecutor.tell(work.job, getSelf());
            getContext().become(working);
          } else unhandled(message);
        }
      };

  private final Procedure<Object> working =
      new Procedure<Object>() {
        public void apply(Object message) {
          if (message instanceof WorkComplete) {
            Object result = ((WorkComplete) message).result;
            log.info("Work is complete. Result {}.", result);
            sendToMaster(new WorkIsDone(workerId, workId(), result));
            getContext().setReceiveTimeout(Duration.create(5, "seconds"));
            getContext().become(waitForWorkIsDoneAck(result));
          } else if (message instanceof Work) {
            log.info("Yikes. Master told me to do work, while I'm working.");
          } else {
            unhandled(message);
          }
        }
      };

  private Procedure<Object> waitForWorkIsDoneAck(final Object result) {
    return new Procedure<Object>() {
      public void apply(Object message) {
        if (message instanceof Ack && ((Ack) message).workId.equals(workId())) {
          sendToMaster(new WorkerRequestsWork(workerId));
          getContext().setReceiveTimeout(Duration.Undefined());
          getContext().become(idle);
        } else if (message instanceof ReceiveTimeout) {
          log.info("No ack from master, retrying (" + workerId + " -> " + workId() + ")");
          sendToMaster(new WorkIsDone(workerId, workId(), result));
        } else {
          unhandled(message);
        }
      }
    };
  }

  {
    getContext().become(idle);
  }

  @Override
  public void unhandled(Object message) {
    if (message instanceof Terminated && ((Terminated) message).getActor().equals(workExecutor)) {
      getContext().stop(getSelf());
    } else if (message instanceof WorkIsReady) {
      // do nothing
    } else {
      super.unhandled(message);
    }
  }

  private void sendToMaster(Object msg) {
    clusterClient.tell(new SendToAll("/user/master/singleton", msg), getSelf());
  }

  public static final class WorkComplete implements Serializable {
    public final Object result;

    public WorkComplete(Object result) {
      this.result = result;
    }

    @Override
    public String toString() {
      return "WorkComplete{" + "result=" + result + '}';
    }
  }
}