/* * Copyright 2014-2016 CyberVision, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaaproject.kaa.server.operations.service.akka; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.routing.Broadcast; import akka.routing.RoundRobinPool; import org.kaaproject.kaa.common.dto.ApplicationDto; import org.kaaproject.kaa.server.common.thrift.gen.operations.Notification; import org.kaaproject.kaa.server.common.thrift.gen.operations.RedirectionRule; import org.kaaproject.kaa.server.operations.service.akka.actors.core.OperationsServerActor; import org.kaaproject.kaa.server.operations.service.akka.actors.io.EncDecActor; import org.kaaproject.kaa.server.operations.service.akka.actors.supervision.SupervisionStrategyFactory; import org.kaaproject.kaa.server.operations.service.akka.messages.core.notification.ThriftNotificationMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.stats.StatusRequestMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.UserConfigurationUpdate; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.UserConfigurationUpdateMessage; import org.kaaproject.kaa.server.sync.platform.PlatformLookup; import org.kaaproject.kaa.server.transport.message.SessionInitMessage; import org.kaaproject.kaa.server.transport.session.SessionAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Set; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Service public class DefaultAkkaService implements AkkaService { public static final String IO_DISPATCHER_NAME = "io-dispatcher"; public static final String CORE_DISPATCHER_NAME = "core-dispatcher"; public static final String USER_DISPATCHER_NAME = "user-dispatcher"; public static final String ENDPOINT_DISPATCHER_NAME = "endpoint-dispatcher"; public static final String LOG_DISPATCHER_NAME = "log-dispatcher"; public static final String VERIFIER_DISPATCHER_NAME = "verifier-dispatcher"; public static final String TOPIC_DISPATCHER_NAME = "topic-dispatcher"; public static final String EPS = "EPS"; private static final String IO_ROUTER_ACTOR_NAME = "ioRouter"; private static final Logger LOG = LoggerFactory.getLogger(DefaultAkkaService.class); private ActorSystem akka; /** * The eps actor. */ private ActorRef opsActor; /** * The io router. */ private ActorRef ioRouter; /** * The akka service context. */ @Autowired private AkkaContext context; private AkkaEventServiceListener eventListener; private AkkaClusterServiceListener clusterListener; private StatusListenerThread statusListenerThread; /** * Inits the actor system. */ @PostConstruct public void initActorSystem() { LOG.info("Initializing Akka system..."); akka = ActorSystem.create(EPS, context.getConfig()); LOG.info("Initializing Akka EPS actor..."); opsActor = akka.actorOf(Props.create( new OperationsServerActor.ActorCreator(context)) .withDispatcher(CORE_DISPATCHER_NAME), EPS); LOG.info("Lookup platform protocols"); Set<String> platformProtocols = PlatformLookup.lookupPlatformProtocols( PlatformLookup.DEFAULT_PROTOCOL_LOOKUP_PACKAGE_NAME); LOG.info("Initializing Akka io router..."); ioRouter = akka.actorOf( new RoundRobinPool(context.getIoWorkerCount()) .withSupervisorStrategy(SupervisionStrategyFactory.createIoRouterStrategy(context)) .props(Props.create(new EncDecActor.ActorCreator(opsActor, context, platformProtocols)) .withDispatcher(IO_DISPATCHER_NAME)), IO_ROUTER_ACTOR_NAME); LOG.info("Initializing Akka event service listener..."); eventListener = new AkkaEventServiceListener(opsActor); context.getEventService().addListener(eventListener); clusterListener = new AkkaClusterServiceListener(opsActor); context.getClusterService().setListener(clusterListener); LOG.info("Initializing Akka system done"); } /* * (non-Javadoc) * * @see * org.kaaproject.kaa.server.operations.service.akka.AkkaService#getActorSystem * () */ @Override public ActorSystem getActorSystem() { return akka; } AkkaEventServiceListener getListener() { return eventListener; } /* * (non-Javadoc) * * @see org.kaaproject.kaa.server.operations.service.akka.AkkaService# * onRedirectionRule * (org.kaaproject.kaa.server.common.thrift.gen.endpoint.RedirectionRule) */ @Override public void onRedirectionRule(RedirectionRule redirectionRule) { ioRouter.tell(new Broadcast(redirectionRule), ActorRef.noSender()); } /* * (non-Javadoc) * * @see * org.kaaproject.kaa.server.operations.service.akka.AkkaService#onNotification * (org.kaaproject.kaa.server.common.thrift.gen.endpoint.Notification) */ @Override public void onNotification(Notification notification) { ApplicationDto applicationDto = context.getApplicationService() .findAppById(notification.getAppId()); if (applicationDto != null) { LOG.debug("Sending message {} to EPS actor", notification); opsActor.tell(new ThriftNotificationMessage( applicationDto.getApplicationToken(), notification), ActorRef.noSender()); } else { LOG.warn("Can't find corresponding application for: {}", notification); } } /** * Remove all event listeners, stop ioRouter and opsActor actors and terminate actor system. */ @PreDestroy public void preDestroy() { context.getEventService().removeListener(eventListener); akka.stop(ioRouter); akka.stop(opsActor); akka.terminate(); } @Override public void process(SessionAware message) { ioRouter.tell(message, ActorRef.noSender()); } @Override public void process(SessionInitMessage message) { ioRouter.tell(message, ActorRef.noSender()); } @Override public void onUserConfigurationUpdate(UserConfigurationUpdate update) { opsActor.tell(new UserConfigurationUpdateMessage(update), ActorRef.noSender()); } @Override public void setStatusListener(final AkkaStatusListener listener, final long statusUpdateFrequency) { this.statusListenerThread = new StatusListenerThread(listener, statusUpdateFrequency); this.statusListenerThread.start(); } @Override public void removeStatusListener() { if (this.statusListenerThread != null) { this.statusListenerThread.stopped = true; this.statusListenerThread.interrupt(); } } public class StatusListenerThread extends Thread { private final AkkaStatusListener listener; private final long statusUpdateFrequency; private volatile boolean stopped = false; /** * Create a new instance of StatusListenerThread. * * @param listener the akka status listener * @param statusUpdateFrequency the status of update frequency */ public StatusListenerThread(AkkaStatusListener listener, long statusUpdateFrequency) { super(); this.listener = listener; this.statusUpdateFrequency = statusUpdateFrequency; } @Override public void run() { while (!stopped) { try { Thread.sleep(statusUpdateFrequency); } catch (InterruptedException ex) { if (!stopped) { LOG.warn("Status update thread was interrupted", ex); } else { break; } } opsActor.tell(new StatusRequestMessage(listener), ActorRef.noSender()); } } } }