// #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; }
/** @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()); } }
/** @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); } }
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())); } }
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()); } }
@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); } }
/** * 계층구조 트리의 루트에 해당하는 최상위 부모 액터 * * @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; } } } } }
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(); } }
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); } }
/** * @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(); }
/** @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(); } } } }
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()); } } }
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); } } }
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()); } } }
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); } } }
// #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(); } }
@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); } }
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); } } }
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); } } }
/** 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); } } }
/** * @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; } }
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); } }
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 + '}'; } } }
/** @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(); } }
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(); }
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 + '}'; } } }
/** @author [email protected] (Thomas Quintana) */ public class MgcpMediaGroup extends MediaGroup { private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); // Finite state machine stuff. private final State uninitialized; private final State active; private final State inactive; // Special intermediate states. private final State acquiringIvr; private final State acquiringLink; private final State initializingLink; private final State openingLink; private final State updatingLink; private final State deactivating; // Join Outboundcall Bridge endpoint to the IVR private final State acquiringInternalLink; private final State initializingInternalLink; private final State openingInternalLink; private final State updatingInternalLink; // FSM. private final FiniteStateMachine fsm; // MGCP runtime stuff. private final ActorRef gateway; private final ActorRef endpoint; private final MediaSession session; private ActorRef link; private ActorRef ivr; private boolean ivrInUse; private MgcpEvent lastEvent; // Runtime stuff. private final List<ActorRef> observers; private ActorRef originator; private ActorRef internalLinkEndpoint; private ActorRef internalLink; private ConnectionMode internalLinkMode; public MgcpMediaGroup( final ActorRef gateway, final MediaSession session, final ActorRef endpoint) { super(); final ActorRef source = self(); // Initialize the states for the FSM. uninitialized = new State("uninitialized", null, null); active = new State("active", new Active(source), null); inactive = new State("inactive", new Inactive(source), null); acquiringIvr = new State("acquiring ivr", new AcquiringIvr(source), null); acquiringLink = new State("acquiring link", new AcquiringLink(source), null); initializingLink = new State("initializing link", new InitializingLink(source), null); openingLink = new State("opening link", new OpeningLink(source), null); updatingLink = new State("updating link", new UpdatingLink(source), null); deactivating = new State("deactivating", new Deactivating(source), null); acquiringInternalLink = new State("acquiring internal link", new AcquiringInternalLink(source), null); initializingInternalLink = new State("initializing internal link", new InitializingInternalLink(source), null); openingInternalLink = new State("opening internal link", new OpeningInternalLink(source), null); updatingInternalLink = new State("updating internal link", new UpdatingInternalLink(source), null); // Initialize the transitions for the FSM. final Set<Transition> transitions = new HashSet<Transition>(); transitions.add(new Transition(uninitialized, acquiringIvr)); transitions.add(new Transition(acquiringIvr, inactive)); transitions.add(new Transition(acquiringIvr, acquiringLink)); transitions.add(new Transition(acquiringLink, inactive)); transitions.add(new Transition(acquiringLink, initializingLink)); transitions.add(new Transition(initializingLink, inactive)); transitions.add(new Transition(initializingLink, openingLink)); transitions.add(new Transition(openingLink, inactive)); transitions.add(new Transition(openingLink, deactivating)); transitions.add(new Transition(openingLink, updatingLink)); transitions.add(new Transition(updatingLink, active)); transitions.add(new Transition(updatingLink, inactive)); transitions.add(new Transition(updatingLink, deactivating)); transitions.add(new Transition(active, deactivating)); transitions.add(new Transition(deactivating, inactive)); transitions.add(new Transition(active, acquiringIvr)); // Join Outbound call Bridge endpoint to IVR endpoint transitions.add(new Transition(active, acquiringInternalLink)); transitions.add(new Transition(acquiringInternalLink, initializingInternalLink)); transitions.add(new Transition(initializingInternalLink, openingInternalLink)); transitions.add(new Transition(openingInternalLink, updatingInternalLink)); transitions.add(new Transition(updatingInternalLink, active)); // Initialize the FSM. this.fsm = new FiniteStateMachine(uninitialized, transitions); // Initialize the MGCP state. this.gateway = gateway; this.session = session; this.endpoint = endpoint; this.ivrInUse = false; // Initialize the rest of the media group state. this.observers = new ArrayList<ActorRef>(); } private void collect(final Object message) { final ActorRef self = self(); final Collect request = (Collect) message; final PlayCollect.Builder builder = PlayCollect.builder(); for (final URI prompt : request.prompts()) { builder.addPrompt(prompt); } builder.setClearDigitBuffer(true); builder.setDigitPattern(request.pattern()); builder.setFirstDigitTimer(request.timeout()); builder.setInterDigitTimer(request.timeout()); builder.setEndInputKey(request.endInputKey()); builder.setMaxNumberOfDigits(request.numberOfDigits()); this.lastEvent = AUMgcpEvent.aupc; stop(lastEvent); this.originator = sender(); ivr.tell(builder.build(), self); ivrInUse = true; } private void play(final Object message) { final ActorRef self = self(); final Play request = (Play) message; final List<URI> uris = request.uris(); final int iterations = request.iterations(); final org.restcomm.connect.mgcp.Play play = new org.restcomm.connect.mgcp.Play(uris, iterations); this.lastEvent = AUMgcpEvent.aupa; stop(lastEvent); this.originator = sender(); ivr.tell(play, self); ivrInUse = true; } @SuppressWarnings("unchecked") private void notification(final Object message) { final IvrEndpointResponse<String> response = (IvrEndpointResponse<String>) message; final ActorRef self = self(); MediaGroupResponse<String> event = null; if (response.succeeded()) { event = new MediaGroupResponse<String>(response.get()); } else { event = new MediaGroupResponse<String>(response.cause(), response.error()); } // for (final ActorRef observer : observers) { // observer.tell(event, self); // } this.originator.tell(event, self); ivrInUse = false; } private void observe(final Object message) { final ActorRef self = self(); final Observe request = (Observe) message; final ActorRef observer = request.observer(); if (observer != null) { observers.add(observer); observer.tell(new Observing(self), self); } } // FSM logic. @Override public void onReceive(final Object message) throws Exception { final Class<?> klass = message.getClass(); final State state = fsm.state(); final ActorRef sender = sender(); if (logger.isInfoEnabled()) { logger.info( "********** Media Group " + self().path() + " Current State: \"" + state.toString()); logger.info( "********** Media Group " + self().path() + " Processing Message: \"" + klass.getName() + " sender : " + sender.getClass()); } if (Observe.class.equals(klass)) { observe(message); } else if (StopObserving.class.equals(klass)) { stopObserving(message); } else if (MediaGroupStatus.class.equals(klass)) { if (active.equals(state)) { sender().tell(new MediaGroupStateChanged(MediaGroupStateChanged.State.ACTIVE), self()); } else { sender().tell(new MediaGroupStateChanged(MediaGroupStateChanged.State.INACTIVE), self()); } } else if (StartMediaGroup.class.equals(klass)) { if (logger.isInfoEnabled()) { logger.info( "MediaGroup: " + self().path() + " got StartMediaGroup from: " + sender().path() + " endpoint: " + endpoint.path() + " isTerminated: " + endpoint.isTerminated()); } fsm.transition(message, acquiringIvr); } else if (Join.class.equals(klass)) { fsm.transition(message, acquiringInternalLink); } else if (MediaGatewayResponse.class.equals(klass)) { if (acquiringIvr.equals(state)) { fsm.transition(message, acquiringLink); } else if (acquiringLink.equals(state)) { fsm.transition(message, initializingLink); } else if (acquiringInternalLink.equals(state)) { fsm.transition(message, initializingInternalLink); } } else if (LinkStateChanged.class.equals(klass)) { final LinkStateChanged response = (LinkStateChanged) message; if (LinkStateChanged.State.CLOSED == response.state()) { if (initializingLink.equals(state)) { fsm.transition(message, openingLink); } else if (openingLink.equals(state) || deactivating.equals(state) || updatingLink.equals(state)) { fsm.transition(message, inactive); } if (initializingInternalLink.equals(state)) { fsm.transition(message, openingInternalLink); } } else if (LinkStateChanged.State.OPEN == response.state()) { if (openingLink.equals(state)) { fsm.transition(message, updatingLink); } else if (updatingLink.equals(state)) { fsm.transition(message, active); } if (openingInternalLink.equals(state)) { fsm.transition(message, updatingInternalLink); } if (updatingInternalLink.equals(state)) { fsm.transition(message, active); } } } else if (StopMediaGroup.class.equals(klass)) { if (acquiringLink.equals(state) || initializingLink.equals(state)) { fsm.transition(message, inactive); } else if (active.equals(state) || openingLink.equals(state) || updatingLink.equals(state)) { fsm.transition(message, deactivating); } } else if (EndpointStateChanged.class.equals(klass)) { onEndpointStateChanged((EndpointStateChanged) message, self(), sender); } else if (active.equals(state)) { if (Play.class.equals(klass)) { play(message); } else if (Collect.class.equals(klass)) { collect(message); } else if (Record.class.equals(klass)) { record(message); } else if (Stop.class.equals(klass)) { stop(lastEvent); // Send message to originator telling media group has been stopped // Needed for call bridging scenario, where inbound call must stop // ringing before attempting to perform join operation. sender().tell(new MediaGroupResponse<String>("stopped"), self()); } else if (IvrEndpointResponse.class.equals(klass)) { notification(message); } } else if (ivrInUse) { if (Stop.class.equals(klass)) { stop(lastEvent); } } } private boolean is(State state) { return state != null && state.equals(this.fsm.state()); } private void onEndpointStateChanged(EndpointStateChanged message, ActorRef self, ActorRef sender) throws Exception { if (is(deactivating)) { if (sender.equals(this.ivr) && EndpointState.DESTROYED.equals(message.getState())) { this.ivr.tell(new StopObserving(self), self); this.fsm.transition(message, inactive); } } } private void record(final Object message) { final ActorRef self = self(); final Record request = (Record) message; final PlayRecord.Builder builder = PlayRecord.builder(); for (final URI prompt : request.prompts()) { builder.addPrompt(prompt); } builder.setClearDigitBuffer(true); builder.setPreSpeechTimer(request.timeout()); builder.setPostSpeechTimer(request.timeout()); builder.setRecordingLength(request.length()); builder.setEndInputKey(request.endInputKey()); builder.setRecordingId(request.destination()); this.lastEvent = AUMgcpEvent.aupr; stop(lastEvent); this.originator = sender(); ivr.tell(builder.build(), self); ivrInUse = true; } private void stop(MgcpEvent signal) { if (ivrInUse) { final ActorRef self = self(); ivr.tell(new StopEndpoint(signal), self); ivrInUse = false; originator = null; } } private void stopObserving(final Object message) { final StopObserving request = (StopObserving) message; final ActorRef observer = request.observer(); if (observer != null) { observers.remove(observer); } } private abstract class AbstractAction implements Action { protected final ActorRef source; public AbstractAction(final ActorRef source) { super(); this.source = source; } } private final class AcquiringIvr extends AbstractAction { public AcquiringIvr(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { if (ivr != null && !ivr.isTerminated()) { if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " got request to create ivr endpoint, will stop the existing one first: " + ivr.path()); } gateway.tell(new DestroyEndpoint(ivr), null); getContext().stop(ivr); ivr = null; } if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " state: " + fsm.state().toString() + " session: " + session.id() + " will ask to get IvrEndpoint"); } gateway.tell(new CreateIvrEndpoint(session), source); } } private final class AcquiringLink extends AbstractAction { public AcquiringLink(final ActorRef source) { super(source); } @SuppressWarnings("unchecked") @Override public void execute(final Object message) throws Exception { final MediaGatewayResponse<ActorRef> response = (MediaGatewayResponse<ActorRef>) message; ivr = response.get(); ivr.tell(new Observe(source), source); if (link != null && !link.isTerminated()) { if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " got request to create link endpoint, will stop the existing one first: " + link.path()); } gateway.tell(new DestroyLink(link), null); getContext().stop(link); } if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " state: " + fsm.state().toString() + " session: " + session.id() + " ivr endpoint: " + ivr.path() + " will ask to get Link"); } gateway.tell(new CreateLink(session), source); } } private final class InitializingLink extends AbstractAction { public InitializingLink(final ActorRef source) { super(source); } @SuppressWarnings("unchecked") @Override public void execute(final Object message) throws Exception { final MediaGatewayResponse<ActorRef> response = (MediaGatewayResponse<ActorRef>) message; link = response.get(); if (endpoint == null) if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " state: " + fsm.state().toString() + " session: " + session.id() + " link: " + link.path() + " endpoint is null will have exception"); } link.tell(new Observe(source), source); link.tell(new InitializeLink(endpoint, ivr), source); if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " state: " + fsm.state().toString() + " session: " + session.id() + " link: " + link.path() + " endpoint: " + endpoint.path() + " initializeLink sent, endpoint isTerminated: " + endpoint.isTerminated()); } } } private final class OpeningLink extends AbstractAction { public OpeningLink(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " state: " + fsm.state().toString() + " session: " + session.id() + " link: " + link.path() + " will ask to open Link"); } link.tell(new OpenLink(ConnectionMode.SendRecv), source); } } private final class UpdatingLink extends AbstractAction { public UpdatingLink(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { final UpdateLink update = new UpdateLink(ConnectionMode.SendRecv, UpdateLink.Type.PRIMARY); link.tell(update, source); } } // Join OutboundCall Bridge endpoint to the IVR endpoint for recording - START private final class AcquiringInternalLink extends AbstractAction { public AcquiringInternalLink(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { final Class<?> klass = message.getClass(); if (Join.class.equals(klass)) { final Join request = (Join) message; internalLinkEndpoint = request.endpoint(); internalLinkMode = request.mode(); } gateway.tell(new CreateLink(session), source); } } private final class InitializingInternalLink extends AbstractAction { public InitializingInternalLink(final ActorRef source) { super(source); } @SuppressWarnings("unchecked") @Override public void execute(Object message) throws Exception { final MediaGatewayResponse<ActorRef> response = (MediaGatewayResponse<ActorRef>) message; internalLink = response.get(); internalLink.tell(new Observe(source), source); internalLink.tell(new InitializeLink(internalLinkEndpoint, ivr), source); } } private final class OpeningInternalLink extends AbstractAction { public OpeningInternalLink(final ActorRef source) { super(source); } @Override public void execute(Object message) throws Exception { internalLink.tell(new OpenLink(internalLinkMode), source); } } private final class UpdatingInternalLink extends AbstractAction { public UpdatingInternalLink(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { final UpdateLink update = new UpdateLink(ConnectionMode.SendRecv, UpdateLink.Type.PRIMARY); internalLink.tell(update, source); } } // Join OutboundCall Bridge endpoint to the IVR endpoint for recording - END private final class Active extends AbstractAction { public Active(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { // Notify the observers. final MediaGroupStateChanged event = new MediaGroupStateChanged(MediaGroupStateChanged.State.ACTIVE); for (final ActorRef observer : observers) { observer.tell(event, source); } } } private final class Inactive extends AbstractAction { public Inactive(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { if (link != null) { gateway.tell(new DestroyLink(link), source); link = null; } if (internalLink != null) { gateway.tell(new DestroyLink(internalLink), source); internalLink = null; } // Notify the observers. final MediaGroupStateChanged event = new MediaGroupStateChanged(MediaGroupStateChanged.State.INACTIVE); for (final ActorRef observer : observers) { observer.tell(event, source); } // // Terminate the actor // getContext().stop(self()); } } private final class Deactivating extends AbstractAction { public Deactivating(final ActorRef source) { super(source); } @Override public void execute(final Object message) throws Exception { ivr.tell(new DestroyEndpoint(), super.source); // if (link != null) // link.tell(new CloseLink(), source); // if (internalLink != null) // internalLink.tell(new CloseLink(), source); } } @Override public void postStop() { if (internalLinkEndpoint != null) { if (logger.isInfoEnabled()) { logger.info( "MediaGroup: " + self().path() + " at postStop, about to stop intenalLinkEndpoint: " + internalLinkEndpoint.path() + " sender: " + sender().path()); } gateway.tell(new DestroyEndpoint(internalLinkEndpoint), null); getContext().stop(internalLinkEndpoint); internalLinkEndpoint = null; } if (ivr != null) { if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " at postStop, about to stop ivr endpoint :" + ivr.path()); } gateway.tell(new DestroyEndpoint(ivr), null); getContext().stop(ivr); ivr = null; } if (link != null) { if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " at postStop, about to stop link :" + link.path()); } getContext().stop(link); link = null; } if (internalLink != null) { if (logger.isInfoEnabled()) { logger.info( "MediaGroup :" + self().path() + " at postStop, about to stop internal link :" + internalLink.path()); } getContext().stop(link); link = null; } getContext().stop(self()); super.postStop(); } }