/**
* 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.store.jdbc;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.falcon.entity.v0.Entity;
import org.apache.falcon.exception.StateStoreException;
import org.apache.falcon.execution.ExecutionInstance;
import org.apache.falcon.persistence.EntityBean;
import org.apache.falcon.persistence.InstanceBean;
import org.apache.falcon.persistence.PersistenceConstants;
import org.apache.falcon.state.EntityClusterID;
import org.apache.falcon.state.EntityID;
import org.apache.falcon.state.EntityState;
import org.apache.falcon.state.ID;
import org.apache.falcon.state.InstanceID;
import org.apache.falcon.state.InstanceState;
import org.apache.falcon.state.store.AbstractStateStore;
import org.apache.falcon.state.store.StateStore;
import org.apache.falcon.service.FalconJPAService;
import org.apache.falcon.util.StateStoreProperties;
import org.joda.time.DateTime;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Persistent Data Store for Entities and Instances.
*/
public final class JDBCStateStore extends AbstractStateStore {
private static final StateStore STORE = new JDBCStateStore();
private static final String DEBUG = "debug";
private JDBCStateStore() {}
public static StateStore get() {
return STORE;
}
@Override
public void clear() throws StateStoreException {
if (!isModeDebug()) {
throw new UnsupportedOperationException("Clear Method not supported");
}
deleteExecutionInstances();
deleteEntities();
}
@Override
public void putEntity(EntityState entityState) throws StateStoreException {
EntityID entityID = new EntityID(entityState.getEntity());
String key = entityID.getKey();
if (entityExists(entityID)) {
throw new StateStoreException("Entity with key, " + key + " already exists.");
}
EntityBean entityBean = null;
try {
entityBean = BeanMapperUtil.convertToEntityBean(entityState);
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
entityManager.persist(entityBean);
commitAndCloseTransaction(entityManager);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public EntityState getEntity(EntityID entityID) throws StateStoreException {
EntityState entityState = getEntityByKey(entityID);
if (entityState == null) {
throw new StateStoreException("Entity with key, " + entityID + " does not exist.");
}
return entityState;
}
private EntityState getEntityByKey(EntityID id) throws StateStoreException {
EntityBean entityBean = getEntityBean(id);
if (entityBean == null) {
return null;
}
try {
return BeanMapperUtil.convertToEntityState(entityBean);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
private EntityBean getEntityBean(EntityID id) {
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_ENTITY);
q.setParameter("id", id.getKey());
List result = q.getResultList();
entityManager.close();
if (result.isEmpty()) {
return null;
}
return ((EntityBean)result.get(0));
}
@Override
public boolean entityExists(EntityID entityID) throws StateStoreException {
return getEntityByKey(entityID) == null ? false : true;
}
@Override
public Collection<Entity> getEntities(EntityState.STATE state) throws StateStoreException {
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_ENTITY_FOR_STATE);
q.setParameter("state", state.toString());
List result = q.getResultList();
entityManager.close();
return BeanMapperUtil.convertToEntities(result);
}
@Override
public Collection<EntityState> getAllEntities() throws StateStoreException {
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_ENTITIES);
List result = q.getResultList();
entityManager.close();
try {
return BeanMapperUtil.convertToEntityState(result);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public void updateEntity(EntityState entityState) throws StateStoreException {
EntityID entityID = new EntityID(entityState.getEntity());
if (!entityExists(entityID)) {
throw new StateStoreException("Entity with key, " + entityID + " doesn't exists.");
}
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
Query q = entityManager.createNamedQuery(PersistenceConstants.UPDATE_ENTITY);
q.setParameter("id", entityID.getKey());
if (entityState.getCurrentState() != null) {
q.setParameter("state", entityState.getCurrentState().toString());
}
q.setParameter("type", entityState.getEntity().getEntityType().toString());
q.setParameter("name", entityState.getEntity().getName());
q.executeUpdate();
commitAndCloseTransaction(entityManager);
}
@Override
public void deleteEntity(EntityID entityID) throws StateStoreException {
if (!entityExists(entityID)) {
throw new StateStoreException("Entity with key, " + entityID.getKey() + " does not exist.");
}
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
Query q = entityManager.createNamedQuery(PersistenceConstants.DELETE_ENTITY);
q.setParameter("id", entityID.getKey());
q.executeUpdate();
commitAndCloseTransaction(entityManager);
}
@Override
public void deleteEntities() throws StateStoreException {
if (!isModeDebug()) {
throw new UnsupportedOperationException("Delete Entities Table not supported");
}
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
Query q = entityManager.createNamedQuery(PersistenceConstants.DELETE_ENTITIES);
q.executeUpdate();
commitAndCloseTransaction(entityManager);
}
@Override
public boolean isEntityCompleted(EntityID entityId) {
// ToDo need to implement this, currently returning false.
return false;
}
@Override
public void putExecutionInstance(InstanceState instanceState) throws StateStoreException {
InstanceID instanceID = new InstanceID(instanceState.getInstance());
if (executionInstanceExists(instanceID)) {
throw new StateStoreException("Instance with key, " + instanceID + " already exists.");
}
try {
InstanceBean instanceBean = BeanMapperUtil.convertToInstanceBean(instanceState);
EntityBean entityBean = getEntityBean(new InstanceID(instanceState.getInstance()).getEntityID());
instanceBean.setEntityBean(entityBean);
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
entityManager.persist(instanceBean);
commitAndCloseTransaction(entityManager);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public InstanceState getExecutionInstance(InstanceID instanceId) throws StateStoreException {
InstanceState instanceState = getExecutionInstanceByKey(instanceId);
if (instanceState == null) {
throw new StateStoreException("Instance with key, " + instanceId.toString() + " does not exist.");
}
return instanceState;
}
private InstanceState getExecutionInstanceByKey(ID instanceKey) throws StateStoreException {
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_INSTANCE);
q.setParameter("id", instanceKey.toString());
List result = q.getResultList();
entityManager.close();
if (result.isEmpty()) {
return null;
}
try {
InstanceBean instanceBean = (InstanceBean)(result.get(0));
return BeanMapperUtil.convertToInstanceState(instanceBean);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public InstanceState getExecutionInstance(String externalID) throws StateStoreException {
if (StringUtils.isEmpty(externalID)) {
throw new StateStoreException("External ID for retrieving instance cannot be null or empty");
}
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_INSTANCE_FOR_EXTERNAL_ID);
q.setParameter("externalID", externalID);
List result = q.getResultList();
entityManager.close();
if (result.isEmpty()) {
return null;
}
try {
InstanceBean instanceBean = (InstanceBean)(result.get(0));
return BeanMapperUtil.convertToInstanceState(instanceBean);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public void updateExecutionInstance(InstanceState instanceState) throws StateStoreException {
InstanceID id = new InstanceID(instanceState.getInstance());
String key = id.toString();
if (!executionInstanceExists(id)) {
throw new StateStoreException("Instance with key, " + key + " does not exist.");
}
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
Query q = entityManager.createNamedQuery(PersistenceConstants.UPDATE_INSTANCE);
ExecutionInstance instance = instanceState.getInstance();
q.setParameter("id", key);
q.setParameter("cluster", instance.getCluster());
q.setParameter("externalID", instance.getExternalID());
q.setParameter("instanceTime", new Timestamp(instance.getInstanceTime().getMillis()));
q.setParameter("creationTime", new Timestamp(instance.getCreationTime().getMillis()));
if (instance.getActualEnd() != null) {
q.setParameter("actualEndTime", new Timestamp(instance.getActualEnd().getMillis()));
}
q.setParameter("currentState", instanceState.getCurrentState().toString());
if (instance.getActualStart() != null) {
q.setParameter("actualStartTime", new Timestamp(instance.getActualStart().getMillis()));
}
q.setParameter("instanceSequence", instance.getInstanceSequence());
if (instanceState.getInstance().getAwaitingPredicates() != null
&& !instanceState.getInstance().getAwaitingPredicates().isEmpty()) {
try {
q.setParameter("awaitedPredicates", BeanMapperUtil.getAwaitedPredicates(instanceState));
} catch (IOException e) {
throw new StateStoreException(e);
}
}
if (instance.getProperties() != null && !instance.getProperties().isEmpty()) {
try {
q.setParameter("properties", BeanMapperUtil.getProperties(instanceState));
} catch (IOException e) {
throw new StateStoreException(e);
}
}
q.executeUpdate();
commitAndCloseTransaction(entityManager);
}
@Override
public Collection<InstanceState> getAllExecutionInstances(Entity entity, String cluster)
throws StateStoreException {
EntityClusterID id = new EntityClusterID(entity, cluster);
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_INSTANCES_FOR_ENTITY_CLUSTER);
q.setParameter("entityId", id.getEntityID().getKey());
q.setParameter("cluster", cluster);
List result = q.getResultList();
entityManager.close();
try {
return BeanMapperUtil.convertToInstanceState(result);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public Collection<InstanceState> getExecutionInstances(Entity entity, String cluster,
Collection<InstanceState.STATE> states)
throws StateStoreException {
EntityClusterID entityClusterID = new EntityClusterID(entity, cluster);
String entityKey = entityClusterID.getEntityID().getKey();
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_INSTANCES_FOR_ENTITY_CLUSTER_FOR_STATES);
q.setParameter("entityId", entityKey);
q.setParameter("cluster", cluster);
List<String> instanceStates = new ArrayList<>();
for (InstanceState.STATE state : states) {
instanceStates.add(state.toString());
}
q.setParameter("currentState", instanceStates);
List result = q.getResultList();
entityManager.close();
try {
return BeanMapperUtil.convertToInstanceState(result);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public Collection<InstanceState> getExecutionInstances(EntityClusterID id,
Collection<InstanceState.STATE> states)
throws StateStoreException {
String entityKey = id.getEntityID().getKey();
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_INSTANCES_FOR_ENTITY_FOR_STATES);
q.setParameter("entityId", entityKey);
List<String> instanceStates = new ArrayList<>();
for (InstanceState.STATE state : states) {
instanceStates.add(state.toString());
}
q.setParameter("currentState", instanceStates);
List result = q.getResultList();
entityManager.close();
try {
return BeanMapperUtil.convertToInstanceState(result);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public Map<InstanceState.STATE, Long> getExecutionInstanceSummary(Entity entity, String cluster,
DateTime start, DateTime end) throws StateStoreException {
String entityKey = new EntityClusterID(entity, cluster).getEntityID().getKey();
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_INSTANCE_SUMMARY_BY_STATE_WITH_RANGE);
q.setParameter("entityId", entityKey);
q.setParameter("cluster", cluster);
q.setParameter("startTime", new Timestamp(start.getMillis()));
q.setParameter("endTime", new Timestamp(end.getMillis()));
List result = q.getResultList();
entityManager.close();
return BeanMapperUtil.getInstanceStateSummary(result);
}
@Override
public Collection<InstanceState> getExecutionInstances(Entity entity, String cluster,
Collection<InstanceState.STATE> states, DateTime start,
DateTime end) throws StateStoreException {
String entityKey = new EntityClusterID(entity, cluster).getEntityID().getKey();
EntityManager entityManager = getEntityManager();
Query q = entityManager.
createNamedQuery(PersistenceConstants.GET_INSTANCES_FOR_ENTITY_CLUSTER_FOR_STATES_WITH_RANGE);
q.setParameter("entityId", entityKey);
List<String> instanceStates = new ArrayList<>();
for (InstanceState.STATE state : states) {
instanceStates.add(state.toString());
}
q.setParameter("currentState", instanceStates);
q.setParameter("startTime", new Timestamp(start.getMillis()));
q.setParameter("endTime", new Timestamp(end.getMillis()));
q.setParameter("cluster", cluster);
List result = q.getResultList();
entityManager.close();
try {
return BeanMapperUtil.convertToInstanceState(result);
} catch (IOException e) {
throw new StateStoreException(e);
}
}
@Override
public InstanceState getLastExecutionInstance(Entity entity, String cluster) throws StateStoreException {
String key = new EntityClusterID(entity, cluster).getEntityID().getKey();
EntityManager entityManager = getEntityManager();
Query q = entityManager.createNamedQuery(PersistenceConstants.GET_LAST_INSTANCE_FOR_ENTITY_CLUSTER);
q.setParameter("entityId", key);
q.setParameter("cluster", cluster);
q.setMaxResults(1);
List result = q.getResultList();
entityManager.close();
if (!result.isEmpty()) {
try {
return BeanMapperUtil.convertToInstanceState((InstanceBean) result.get(0));
} catch (IOException e) {
throw new StateStoreException(e);
}
}
return null;
}
@Override
public boolean executionInstanceExists(InstanceID instanceKey) throws StateStoreException {
return getExecutionInstanceByKey(instanceKey) == null ? false : true;
}
@Override
public void deleteExecutionInstance(InstanceID instanceID) throws StateStoreException {
String instanceKey = instanceID.toString();
if (!executionInstanceExists(instanceID)) {
throw new StateStoreException("Instance with key, " + instanceKey + " does not exist.");
}
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
Query q = entityManager.createNamedQuery(PersistenceConstants.DELETE_INSTANCE);
q.setParameter("id", instanceKey);
q.executeUpdate();
commitAndCloseTransaction(entityManager);
}
@Override
public void deleteExecutionInstances(EntityID entityID) {
String entityKey = entityID.getKey();
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
Query q = entityManager.createNamedQuery(PersistenceConstants.DELETE_INSTANCE_FOR_ENTITY);
q.setParameter("entityId", entityKey);
q.executeUpdate();
commitAndCloseTransaction(entityManager);
}
@Override
public void deleteExecutionInstances() {
if (!isModeDebug()) {
throw new UnsupportedOperationException("Delete Instances Table not supported");
}
EntityManager entityManager = getEntityManager();
beginTransaction(entityManager);
Query q = entityManager.createNamedQuery(PersistenceConstants.DELETE_INSTANCES_TABLE);
q.executeUpdate();
commitAndCloseTransaction(entityManager);
}
// Debug enabled for test cases
private boolean isModeDebug() {
return DEBUG.equals(StateStoreProperties.get().getProperty("domain")) ? true : false;
}
private void commitAndCloseTransaction(EntityManager entityManager) {
entityManager.getTransaction().commit();
entityManager.close();
}
private void beginTransaction(EntityManager entityManager) {
entityManager.getTransaction().begin();
}
private EntityManager getEntityManager() {
return FalconJPAService.get().getEntityManager();
}
}