All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.statefulj.persistence.common.AbstractPersister Maven / Gradle / Ivy

/***
 * 
 * Copyright 2014 Andrew Hall
 *
 * 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.statefulj.persistence.common;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.statefulj.common.utils.ReflectionUtils;
import org.statefulj.fsm.Persister;
import org.statefulj.fsm.StaleStateException;
import org.statefulj.fsm.model.State;

public abstract class AbstractPersister implements Persister {
	
	private static final Logger logger = LoggerFactory.getLogger(AbstractPersister.class);
	
	private Field idField;
	private Field stateField;
	private State start;
	private Class clazz;
	private HashMap> states = new HashMap>();
	
	public AbstractPersister(
			List> states, 
			String stateFieldName, 
			State start, 
			Class clazz) {
		
		this.clazz = clazz;
		
		// Find the Id and State field of the Entity
		//
		this.idField = findIdField(clazz);
		
		if (this.idField == null) {
			throw new RuntimeException("No Id field defined");
		}
		this.idField.setAccessible(true);
		
		this.stateField = findStateField(stateFieldName, clazz);

		if (this.stateField == null) {
			throw new RuntimeException("No State field defined");
		}
		
		if (!validStateField(this.stateField)) {
			throw new RuntimeException(
					String.format(
							"State field, %s, of class %s, is not of type %s",
							this.stateField.getName(),
							clazz,
							getStateFieldType()));
		}

		this.stateField.setAccessible(true);

		// Start state - returned when no state is set
		//
		this.start = start;
		
		// Index States into a HashMap
		//
		for(State state : states) {
			this.states.put(state.getName(), state);
		}
	}

	@Override
	public State getCurrent(T stateful) {
		State state = null;
		try {
			String stateKey = this.getState(stateful);
			state = (stateKey == null) ? this.start : this.states.get(stateKey);
		} catch (NoSuchFieldException e) {
			throw new RuntimeException(e);
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		state = (state == null) ? this.start : state;
		return state;
	}

	/**
	 * Set the current State.  This method will ensure that the state in the db matches the expected current state.  
	 * If not, it will throw a StateStateException
	 * 
	 * @param stateful Stateful Entity
	 * @param current Expected current State
	 * @param next The value of the next State
	 * @throws StaleStateException thrown if the value of the State does not equal to the provided current State
	 */
	@Override
	public abstract void setCurrent(T stateful, State current, State next) throws StaleStateException;

	protected abstract boolean validStateField(Field stateField); 
	
	protected abstract Field findIdField(Class clazz); 

	protected Field findStateField(String stateFieldName, Class clazz) {
		Field stateField = null;
		if (StringUtils.isEmpty(stateFieldName)) {
			stateField = ReflectionUtils.getFirstAnnotatedField(clazz, org.statefulj.persistence.annotations.State.class);
		} else {
			try {
				stateField = clazz.getDeclaredField(stateFieldName);
			} catch (NoSuchFieldException e) {
				logger.error("Unable to locate state field for {}, stateFieldName={}", clazz.getName(), stateFieldName);
			} catch (SecurityException e) {
				logger.error("Security exception trying to locate state field for {}, stateFieldName={}", clazz.getName(), stateFieldName);
				logger.error("Exception", e);
			}
		}
		return stateField;
	}
	
	protected abstract Class getStateFieldType(); 

	protected Field getIdField() {
		return idField;
	}

	protected void setIdField(Field idField) {
		this.idField = idField;
	}

	protected Field getStateField() {
		return stateField;
	}

	protected void setStateField(Field stateField) {
		this.stateField = stateField;
	}

	protected State getStart() {
		return start;
	}

	protected void setStart(State start) {
		this.start = start;
	}

	protected Class getClazz() {
		return clazz;
	}

	protected void setClazz(Class clazz) {
		this.clazz = clazz;
	}

	protected HashMap> getStates() {
		return states;
	}

	protected void setStates(HashMap> states) {
		this.states = states;
	}

	protected Object getId(T obj) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		return this.idField.get(obj);
	}
	
	protected String getState(T obj) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		return (String)this.stateField.get(obj);
	}
	
	protected void setState(T obj, String state) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
		state = (state == null) ? this.start.getName() : state;
		this.stateField.set(obj, state);
	}

	protected void throwStaleState(State current, State next) throws StaleStateException {
		String err = String.format(
				"Unable to update state, entity.state=%s, db.state=%s",
				current.getName(),
				next.getName());
		throw new StaleStateException(err);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy