/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.falcon.state; import org.apache.falcon.FalconException; import org.apache.falcon.entity.v0.Entity; import org.apache.falcon.entity.v0.EntityType; import org.apache.falcon.execution.ExecutionInstance; import org.apache.falcon.execution.ProcessExecutionInstance; import org.apache.falcon.state.store.AbstractStateStore; import org.apache.falcon.state.store.StateStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Properties; /** * A service that fetches state from state store, handles state transitions of entities and instances, * invokes state change handler and finally persists the new state in the state store. */ public final class StateService { private static final Logger LOG = LoggerFactory.getLogger(StateService.class); private static final StateService LIFE_CYCLE_SERVICE = new StateService(); private final StateStore stateStore; private StateService() { stateStore = AbstractStateStore.get(); } /** * @return - Singleton instance of StateService */ public static StateService get() { return LIFE_CYCLE_SERVICE; } /** * @return - Name of the service */ public String getName() { return "EntityLifeCycleService"; } /** * Fetches the entity from state store, applies state transitions, calls appropriate method on the handler and * persists the final state in the store. * * @param entity * @param event * @param handler * @throws FalconException */ public void handleStateChange(Entity entity, EntityState.EVENT event, EntityStateChangeHandler handler, Properties props) throws FalconException { EntityID id = new EntityID(entity); if (!stateStore.entityExists(id)) { // New entity if (event == EntityState.EVENT.SUBMIT) { callbackHandler(entity, EntityState.EVENT.SUBMIT, handler); EntityState entityState = new EntityState(entity); if (props != null && !props.isEmpty()) { entityState.setProperties(props); } stateStore.putEntity(entityState); LOG.debug("Entity {} submitted due to event {}.", id, event.name()); } else { throw new FalconException("Entity " + id + " does not exist in state store."); } } else { if (entity.getEntityType() == EntityType.CLUSTER) { throw new FalconException("Cluster entity " + entity.getName() + " can only be submitted."); } EntityState entityState = stateStore.getEntity(id); EntityState.STATE newState = entityState.nextTransition(event); callbackHandler(entity, event, handler); if (newState != entityState.getCurrentState()) { entityState.setCurrentState(newState); stateStore.updateEntity(entityState); LOG.debug("State of entity: {} changed to: {} as a result of event: {}.", id, entityState.getCurrentState(), event.name()); } } } public void handleStateChange(Entity entity, EntityState.EVENT event, EntityStateChangeHandler handler) throws FalconException { handleStateChange(entity, event, handler, null); } // Invokes the right method on the state change handler private void callbackHandler(Entity entity, EntityState.EVENT event, EntityStateChangeHandler handler) throws FalconException { if (handler == null) { return; } switch (event) { case SUBMIT: handler.onSubmit(entity); break; case SCHEDULE: handler.onSchedule(entity); break; case SUSPEND: handler.onSuspend(entity); break; case RESUME: handler.onResume(entity); break; case KILL: handler.onKill(entity); break; default: // Do nothing, only propagate events that originate from user } } /** * Fetches the instance from state store, applies state transitions, calls appropriate method on the handler and * persists the final state in the store. * * @param instance * @param event * @param handler * @throws FalconException */ public void handleStateChange(ExecutionInstance instance, InstanceState.EVENT event, InstanceStateChangeHandler handler) throws FalconException { InstanceID id = new InstanceID(instance); if (!stateStore.executionInstanceExists(id)) { // New instance if (event == InstanceState.EVENT.TRIGGER) { callbackHandler(instance, InstanceState.EVENT.TRIGGER, handler); stateStore.putExecutionInstance(new InstanceState(instance)); ((ProcessExecutionInstance) instance).registerForNotifications(false); LOG.debug("Instance {} triggered due to event {}.", id, event.name()); } else if (event == InstanceState.EVENT.EXTERNAL_TRIGGER) { callbackHandler(instance, InstanceState.EVENT.EXTERNAL_TRIGGER, handler); stateStore.updateExecutionInstance(new InstanceState(instance)); LOG.debug("Instance {} triggered due to event {}.", id, event.name()); } else { throw new FalconException("Instance " + id + "does not exist."); } } else { InstanceState instanceState = stateStore.getExecutionInstance(id); InstanceState.STATE newState = instanceState.nextTransition(event); callbackHandler(instance, event, handler); instanceState = new InstanceState(instance); instanceState.setCurrentState(newState); stateStore.updateExecutionInstance(instanceState); LOG.debug("State of instance: {} changed to: {} as a result of event: {}.", id, instanceState.getCurrentState(), event.name()); } } // Invokes the right method on the state change handler private void callbackHandler(ExecutionInstance instance, InstanceState.EVENT event, InstanceStateChangeHandler handler) throws FalconException { if (handler == null) { return; } switch (event) { case TRIGGER: handler.onTrigger(instance); break; case EXTERNAL_TRIGGER: handler.onExternalTrigger(instance); break; case CONDITIONS_MET: handler.onConditionsMet(instance); break; case TIME_OUT: handler.onTimeOut(instance); break; case SCHEDULE: handler.onSchedule(instance); break; case SUSPEND: handler.onSuspend(instance); break; case RESUME_WAITING: case RESUME_READY: case RESUME_RUNNING: handler.onResume(instance); break; case KILL: handler.onKill(instance); break; case SUCCEED: handler.onSuccess(instance); break; case FAIL: handler.onFailure(instance); break; default: // Do nothing } } }