/* * 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.actors.core; import static org.kaaproject.kaa.server.operations.service.akka.DefaultAkkaService.CORE_DISPATCHER_NAME; import akka.actor.ActorRef; import akka.actor.Props; import akka.actor.SupervisorStrategy; import akka.actor.UntypedActor; import akka.japi.Creator; import org.kaaproject.kaa.server.operations.service.akka.AkkaContext; import org.kaaproject.kaa.server.operations.service.akka.AkkaServiceStatus; import org.kaaproject.kaa.server.operations.service.akka.AkkaStatusListener; import org.kaaproject.kaa.server.operations.service.akka.actors.supervision.SupervisionStrategyFactory; import org.kaaproject.kaa.server.operations.service.akka.messages.core.endpoint.EndpointAwareMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.lb.ClusterUpdateMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.notification.ThriftNotificationMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.route.EndpointActorMsg; import org.kaaproject.kaa.server.operations.service.akka.messages.core.stats.StatusRequestMessage; import org.kaaproject.kaa.server.operations.service.akka.messages.core.stats.StatusRequestState; import org.kaaproject.kaa.server.operations.service.akka.messages.core.stats.TenantActorStatusResponse; import org.kaaproject.kaa.server.operations.service.akka.messages.core.user.TenantAwareMessage; import org.kaaproject.kaa.server.transport.message.SessionControlMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class OperationsServerActor extends UntypedActor { private static final Logger LOG = LoggerFactory.getLogger(OperationsServerActor.class); private final AkkaContext context; private final Map<String, ActorRef> tenants; private final Map<UUID, StatusRequestState> statusRequestStatesMap; /** * Instantiates a new endpoint server actor. * * @param context the akka service context */ private OperationsServerActor(AkkaContext context) { super(); this.context = context; this.tenants = new HashMap<>(); this.statusRequestStatesMap = new HashMap<>(); } @Override public SupervisorStrategy supervisorStrategy() { return SupervisionStrategyFactory.createOpsActorStrategy(context); } /* * (non-Javadoc) * * @see akka.actor.UntypedActor#onReceive(java.lang.Object) */ @Override public void onReceive(Object message) throws Exception { LOG.debug("Received: {}", message); if (message instanceof EndpointActorMsg) { processEndpointActorMsg((EndpointActorMsg) message); } else if (message instanceof EndpointAwareMessage) { processEndpointAwareMessage((EndpointAwareMessage) message); } else if (message instanceof SessionControlMessage) { processSessionControlMessage((SessionControlMessage) message); } else if (message instanceof TenantAwareMessage) { processTenantAwareMessage((TenantAwareMessage) message); } else if (message instanceof ThriftNotificationMessage) { processNotificationMessage((ThriftNotificationMessage) message); } else if (message instanceof ClusterUpdateMessage) { processClusterUpdate((ClusterUpdateMessage) message); } else if (message instanceof StatusRequestMessage) { processStatusRequest((StatusRequestMessage) message); } else if (message instanceof TenantActorStatusResponse) { processStatusResponse((TenantActorStatusResponse) message); } } private void processTenantAwareMessage(TenantAwareMessage message) { ActorRef tenantActor = getOrCreateTenantActorByTokenId(message.getTenantId()); tenantActor.tell(message, self()); } /** * Process notification message. * * @param message the message */ private void processNotificationMessage(ThriftNotificationMessage message) { ActorRef tenantActor = getOrCreateTenantActorByAppToken(message.getAppToken()); tenantActor.tell(message, self()); } private void processEndpointActorMsg(EndpointActorMsg message) { ActorRef tenantActor = getOrCreateTenantActorByTokenId(message.getAddress().getTenantId()); tenantActor.tell(message, self()); } /** * Process endpoint aware message. * * @param message the message */ private void processEndpointAwareMessage(EndpointAwareMessage message) { ActorRef tenantActor = getOrCreateTenantActorByAppToken(message.getAppToken()); tenantActor.tell(message, self()); } private void processSessionControlMessage(SessionControlMessage message) { ActorRef tenantActor = getOrCreateTenantActorByAppToken( message.getSessionInfo().getApplicationToken()); tenantActor.tell(message, self()); } private void processClusterUpdate(ClusterUpdateMessage message) { for (ActorRef tenantActor : tenants.values()) { tenantActor.tell(message, ActorRef.noSender()); } } private void processStatusRequest(StatusRequestMessage message) { LOG.debug("[{}] Processing status request", message.getId()); if (tenants.size() > 0) { statusRequestStatesMap.put(message.getId(), new StatusRequestState(message, tenants.size())); for (ActorRef tenant : tenants.values()) { tenant.tell(new StatusRequestMessage(message.getId()), this.getSelf()); } } else { if (message.getListener() != null) { message.getListener().onStatusUpdate(new AkkaServiceStatus(System.currentTimeMillis(), 0)); } } } private void processStatusResponse(TenantActorStatusResponse message) { StatusRequestState state = statusRequestStatesMap.get(message.getRequestId()); if (state != null) { if (state.processResponse(message)) { int endpointCount = state.getEndpontCount(); AkkaStatusListener listener = state.getOriginator().getListener(); if (listener != null) { listener.onStatusUpdate(new AkkaServiceStatus(System.currentTimeMillis(), endpointCount)); } else { LOG.warn("[{}] Calculated state for empty listener", message.getRequestId()); } statusRequestStatesMap.remove(message.getRequestId()); } } else { LOG.warn("[{}] State for status request is not found", message.getRequestId()); } } /** * Gets the or create application actor. * * @param appToken the app token * @return the or create application actor */ private ActorRef getOrCreateTenantActorByAppToken(String appToken) { String tenantId = context.getCacheService().getTenantIdByAppToken(appToken); return getOrCreateTenantActorByTokenId(tenantId); } private ActorRef getOrCreateTenantActorByTokenId(String tenantId) { ActorRef tenantActor = tenants.get(tenantId); if (tenantActor == null) { tenantActor = context().actorOf( Props.create(new TenantActor.ActorCreator(context, tenantId)) .withDispatcher(CORE_DISPATCHER_NAME), tenantId); tenants.put(tenantId, tenantActor); } return tenantActor; } /* * (non-Javadoc) * * @see akka.actor.UntypedActor#preStart() */ @Override public void preStart() { LOG.info("Starting {}", this); } /* * (non-Javadoc) * * @see akka.actor.UntypedActor#postStop() */ @Override public void postStop() { LOG.info("Stoped {}", this); } public static class ActorCreator implements Creator<OperationsServerActor> { private static final long serialVersionUID = 1L; private final AkkaContext context; /** * Instantiates a new actor creator. * * @param context - the Akka service context */ public ActorCreator(AkkaContext context) { super(); this.context = context; } /* * (non-Javadoc) * * @see akka.japi.Creator#create() */ @Override public OperationsServerActor create() throws Exception { return new OperationsServerActor(context); } } }