/**
* 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;
import com.google.common.collect.Lists;
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.state.EntityClusterID;
import org.apache.falcon.state.EntityID;
import org.apache.falcon.state.EntityState;
import org.apache.falcon.state.InstanceID;
import org.apache.falcon.state.InstanceState;
import org.joda.time.DateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* An in memory state store mostly intended for unit tests.
* Singleton.
*/
public final class InMemoryStateStore extends AbstractStateStore {
private Map<String, EntityState> entityStates = new HashMap<>();
// Keep it sorted
private SortedMap<String, InstanceState> instanceStates = Collections
.synchronizedSortedMap(new TreeMap<String, InstanceState>());
private static final StateStore STORE = new InMemoryStateStore();
private InMemoryStateStore() {}
public static StateStore get() {
return STORE;
}
@Override
public void putEntity(EntityState entityState) throws StateStoreException {
String key = new EntityID(entityState.getEntity()).getKey();
if (entityStates.containsKey(key)) {
throw new StateStoreException("Entity with key, " + key + " already exists.");
}
entityStates.put(key, entityState);
}
@Override
public EntityState getEntity(EntityID entityId) throws StateStoreException {
if (!entityStates.containsKey(entityId.getKey())) {
throw new StateStoreException("Entity with key, " + entityId + " does not exist.");
}
return entityStates.get(entityId.getKey());
}
@Override
public boolean entityExists(EntityID entityId) {
return entityStates.containsKey(entityId.getKey());
}
@Override
public Collection<Entity> getEntities(EntityState.STATE state) {
Collection<Entity> entities = new ArrayList<>();
for (EntityState entityState : entityStates.values()) {
if (entityState.getCurrentState().equals(state)) {
entities.add(entityState.getEntity());
}
}
return entities;
}
@Override
public Collection<EntityState> getAllEntities() {
return entityStates.values();
}
@Override
public void updateEntity(EntityState entityState) throws StateStoreException {
String key = new EntityID(entityState.getEntity()).getKey();
if (!entityStates.containsKey(key)) {
throw new StateStoreException("Entity with key, " + key + " does not exist.");
}
entityStates.put(key, entityState);
}
@Override
public void deleteEntity(EntityID entityId) throws StateStoreException {
if (!entityStates.containsKey(entityId.getKey())) {
throw new StateStoreException("Entity with key, " + entityId + " does not exist.");
}
deleteExecutionInstances(entityId);
entityStates.remove(entityId.getKey());
}
@Override
public void deleteEntities() throws StateStoreException {
entityStates.clear();
}
@Override
public boolean isEntityCompleted(EntityID entityId) {
// ToDo need to implement this, currently returning false.
return false;
}
@Override
public void putExecutionInstance(InstanceState instanceState) throws StateStoreException {
String key = new InstanceID(instanceState.getInstance()).getKey();
if (instanceStates.containsKey(key)) {
throw new StateStoreException("Instance with key, " + key + " already exists.");
}
instanceStates.put(key, instanceState);
}
@Override
public InstanceState getExecutionInstance(InstanceID instanceId) throws StateStoreException {
if (!instanceStates.containsKey(instanceId.getKey())) {
throw new StateStoreException("Instance with key, " + instanceId + " does not exist.");
}
return instanceStates.get(instanceId.toString());
}
@Override
public InstanceState getExecutionInstance(String externalID) throws StateStoreException {
if (StringUtils.isEmpty(externalID)) {
throw new StateStoreException("External ID for retrieving instance cannot be null");
}
for (InstanceState instanceState : instanceStates.values()) {
if (externalID.equals(instanceState.getInstance().getExternalID())) {
return instanceState;
}
}
return null;
}
@Override
public void updateExecutionInstance(InstanceState instanceState) throws StateStoreException {
String key = new InstanceID(instanceState.getInstance()).getKey();
if (!instanceStates.containsKey(key)) {
throw new StateStoreException("Instance with key, " + key + " does not exist.");
}
instanceStates.put(key, instanceState);
}
@Override
public Collection<InstanceState> getAllExecutionInstances(Entity entity, String cluster)
throws StateStoreException {
EntityClusterID id = new EntityClusterID(entity, cluster);
if (!entityStates.containsKey(id.getEntityID().getKey())) {
throw new StateStoreException("Entity with key, " + id.getEntityID().getKey() + " does not exist.");
}
Collection<InstanceState> instances = new ArrayList<>();
for (Map.Entry<String, InstanceState> instanceState : instanceStates.entrySet()) {
if (instanceState.getKey().startsWith(id.toString())) {
instances.add(instanceState.getValue());
}
}
return instances;
}
@Override
public Collection<InstanceState> getExecutionInstances(Entity entity, String cluster,
Collection<InstanceState.STATE> states) throws StateStoreException {
EntityClusterID id = new EntityClusterID(entity, cluster);
return getExecutionInstances(id, states);
}
@Override
public Collection<InstanceState> getExecutionInstances(Entity entity, String cluster,
Collection<InstanceState.STATE> states, DateTime start, DateTime end) throws StateStoreException {
List<InstanceState> instancesToReturn = new ArrayList<>();
EntityClusterID id = new EntityClusterID(entity, cluster);
for (InstanceState state : getExecutionInstances(id, states)) {
ExecutionInstance instance = state.getInstance();
DateTime instanceTime = instance.getInstanceTime();
// Start date inclusive and end date exclusive.
// If start date and end date are equal no instances will be added.
if ((instanceTime.isEqual(start) || instanceTime.isAfter(start))
&& instanceTime.isBefore(end)) {
instancesToReturn.add(state);
}
}
return instancesToReturn;
}
@Override
public Collection<InstanceState> getExecutionInstances(EntityClusterID entityId,
Collection<InstanceState.STATE> states) throws StateStoreException {
Collection<InstanceState> instances = new ArrayList<>();
for (Map.Entry<String, InstanceState> instanceState : instanceStates.entrySet()) {
if (instanceState.getKey().startsWith(entityId.toString())
&& states.contains(instanceState.getValue().getCurrentState())) {
instances.add(instanceState.getValue());
}
}
return instances;
}
@Override
public Map<InstanceState.STATE, Long> getExecutionInstanceSummary(Entity entity, String cluster,
DateTime start, DateTime end) throws StateStoreException {
Map<InstanceState.STATE, Long> summary = new HashMap<>();
for (InstanceState state : getAllExecutionInstances(entity, cluster)) {
ExecutionInstance instance = state.getInstance();
DateTime instanceTime = instance.getInstanceTime();
// Start date inclusive and end date exclusive.
// If start date and end date are equal no instances will be added.
if ((instanceTime.isEqual(start) || instanceTime.isAfter(start))
&& instanceTime.isBefore(end)) {
if (summary.containsKey(state.getCurrentState())) {
summary.put(state.getCurrentState(), summary.get(state.getCurrentState()) + 1L);
} else {
summary.put(state.getCurrentState(), 1L);
}
}
}
return summary;
}
@Override
public InstanceState getLastExecutionInstance(Entity entity, String cluster) throws StateStoreException {
EntityClusterID id = new EntityClusterID(entity, cluster);
if (!entityStates.containsKey(id.getEntityID().getKey())) {
throw new StateStoreException("Entity with key, " + id.getEntityID().getKey() + " does not exist.");
}
InstanceState latestState = null;
// TODO : Very crude. Iterating over all entries and getting the last one.
for (Map.Entry<String, InstanceState> instanceState : instanceStates.entrySet()) {
if (instanceState.getKey().startsWith(id.toString())) {
latestState = instanceState.getValue();
}
}
return latestState;
}
@Override
public boolean executionInstanceExists(InstanceID instanceId) {
return instanceStates.containsKey(instanceId.toString());
}
@Override
public void deleteExecutionInstances(EntityID entityId) {
for (String instanceKey : Lists.newArrayList(instanceStates.keySet())) {
if (instanceKey.startsWith(entityId.getKey())) {
instanceStates.remove(instanceKey);
}
}
}
@Override
public void deleteExecutionInstances() {
instanceStates.clear();
}
@Override
public void deleteExecutionInstance(InstanceID instanceID) throws StateStoreException {
if (!instanceStates.containsKey(instanceID.toString())) {
throw new StateStoreException("Instance with key, " + instanceID.toString() + " does not exist.");
}
instanceStates.remove(instanceID.toString());
}
@Override
public void clear() {
entityStates.clear();
instanceStates.clear();
}
}