Skip to content
Snippets Groups Projects
Verified Commit af328e68 authored by Timm Fitschen's avatar Timm Fitschen
Browse files

WIP: FSM

parent e8a64dd5
Branches
Tags
3 merge requests!21Release v0.4.0,!7F fsm,!6Draft: F acm permissions2
......@@ -41,4 +41,14 @@ public class ClientMessage extends Message {
public String getProperty(String key) {
return properties.get(key);
}
@Override
public String toString() {
return this.type + " - " + this.properties.toString();
}
@Override
public int hashCode() {
return type.hashCode() + this.properties.hashCode();
}
}
......@@ -225,14 +225,6 @@ public abstract class Job {
}
}
// @Override
// public boolean notifyObserver(final String e, final Observable o) {
// if (getEntity().getEntityStatus() != EntityStatus.UNQUALIFIED) {
// getTransaction().getSchedule().runJob(this);
// }
// return true;
// }
static HashMap<String, Class<? extends Job>> allClasses = null;
private static List<Class<? extends Job>> loadAlways;
......
......@@ -16,28 +16,41 @@ public class CheckStateTransition extends EntityStateJob {
private static final Message TRANSITION_NOT_ALLOWED =
new Message(MessageType.Error, "Transition not allowed.");
private static final Message INITIAL_STATE_NOT_ALLOWED =
new Message(MessageType.Error, "Initial state not allowed.");
private static final Message FINAL_STATE_NOT_ALLOWED =
new Message(MessageType.Error, "Final state not allowed.");
private static final String FORCE_FINAL_STATE = "forceFinalState";
@Override
protected void run() {
try {
State nextState = getState();
if (nextState != null) {
checkStateValid(nextState);
}
if (getEntity() instanceof UpdateEntity) {
StateMessage toState = getStateMessage();
StateMessage fromState = getStateMessage(((UpdateEntity) getEntity()).getOriginal());
checkStateTransition(fromState, toState);
State fromState = getState(((UpdateEntity) getEntity()).getOriginal());
checkStateTransition(fromState, nextState);
} else if (getEntity() instanceof DeleteEntity) {
StateMessage finalState = getStateMessage();
checkFinalState(finalState);
if (nextState != null) checkFinalState(nextState);
} else {
StateMessage initialState = getStateMessage();
checkInitialState(initialState);
if (nextState != null) checkInitialState(nextState);
}
} catch (Message m) {
getEntity().addError(m);
}
}
private void checkStateValid(State state) throws Message {
if (state.isFinal() || state.isInitial() || state.getStateModel().getStates().contains(state)) {
return;
}
throw STATE_NOT_IN_STATE_MODEL;
}
/** Check if state is valid and transition is allowed */
private void checkStateTransition(StateMessage fromState, StateMessage toState) throws Message {
private void checkStateTransition(State fromState, State toState) throws Message {
if (fromState == null && toState == null) {
return;
} else if (fromState == null && toState != null) {
......@@ -50,32 +63,44 @@ public class CheckStateTransition extends EntityStateJob {
StateModel stateModel = findMatchingStateModel(fromState, toState);
if (stateModel == null) {
// change from one stateModel to another
checkInitialState(toState);
checkFinalState(fromState);
return;
}
for (Transition t : stateModel.getTransitions()) {
if (t.fromStatesInclude(fromState.getState()) && t.toStatesInclude(toState.getState())) {
if (t.fromStatesInclude(fromState) && t.toStatesInclude(toState)) {
// TODO permissions
return;
}
}
throw TRANSITION_NOT_ALLOWED;
}
private StateModel findMatchingStateModel(StateMessage fromState, StateMessage toState)
throws Message {
private StateModel findMatchingStateModel(State fromState, State toState) throws Message {
if (fromState.getStateModel().equals(toState.getStateModel())) {
return fromState.getStateModel();
}
return null;
}
private void checkFinalState(StateMessage fromState) {
// TODO Auto-generated method stub
private void checkFinalState(State fromState) throws Message {
if (!fromState.isFinal()) {
if ("true"
.equalsIgnoreCase(getTransaction().getContainer().getFlags().get(FORCE_FINAL_STATE))) {
// TODO permissions
return;
}
throw FINAL_STATE_NOT_ALLOWED;
}
// TODO permissions
}
private void checkInitialState(StateMessage toState) {
// TODO Auto-generated method stub
private void checkInitialState(State toState) throws Message {
if (!toState.isInitial()) {
throw INITIAL_STATE_NOT_ALLOWED;
}
// TODO permissions
}
}
package org.caosdb.server.jobs.core;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
......@@ -24,24 +25,81 @@ import org.caosdb.server.jobs.EntityJob;
import org.caosdb.server.query.Query;
import org.jdom2.Element;
/**
* The EntityStateJob is the abstract base class for four EntityJobs.
*
* <p>1. The InitEntityState job reads ClientMessages or StateProperties with tag state and converts
* them into instances of State. This job runs during WriteTransactions. This job runs during the
* INIT Phase and does not perform any checks other than those necessary for the conversion.
*
* <p>2. The CheckStateTransition job checks if the attempted state transition is in compliance with
* the state model. This job runs during the CHECK phase and should do all necessary consistency and
* permission checks.
*
* <p>3. The MakeStateProperty job constructs an ordinary Property from the State right before the
* entity is being written to the back-end and after any checks run.
*
* <p>4. The MakeStateMessage job converts a state property (back) into State messages and appends
* them to the entity.
*
* <p>Only the 4th job runs during Retrieve transitions. During WriteTransactions all four jobs do
* run.
*
* @author Timm Fitschen (t.fitschen@indiscale.com)
*/
public abstract class EntityStateJob extends EntityJob {
public static final String TO_STATE_PROPERTY_NAME = "to";
public static final String FROM_STATE_PROPERTY_NAME = "from";
public static final String FINAL_STATE_PROPERTY_NAME = "final";
public static final String INITIAL_STATE_PROPERTY_NAME = "final";
public static final String STATE_RECORD_TYPE_NAME = "State";
public static final String STATE_MODEL_RECORD_TYPE_NAME = "StateModel";
public static final String TRANSITION_RECORD_TYPE_NAME = "Transition";
public static final String STATE_XML_TAG = "State";
public static final String MODEL_ATTRIBUTE_NAME = "model";
public static final String STATE_ATTRIBUTE_NAME = "name";
public static final Message STATE_MODEL_NOT_FOUND =
new Message(MessageType.Error, "StateModel not found.");
public static final Message STATE_NOT_IN_STATE_MODEL =
new Message(MessageType.Error, "State does not exist in this StateModel.");
public static final Message COULD_NOT_GENERATE_STATE_MESSAGE =
new Message(MessageType.Error, "Could not generate the state message.");
public static final Message COULD_NOT_GENERATE_TRANSITIONS =
new Message(MessageType.Error, "Could not generate transitions.");
public static final Message COULD_NOT_GENERATE_STATES =
new Message(MessageType.Error, "Could not generate states.");
public static final Message STATE_MODEL_NOT_SPECIFIED =
new Message(MessageType.Error, "State model not specified.");
public static final Message STATE_NOT_SPECIFIED =
new Message(MessageType.Error, "State not specified.");
/**
* Represents a Transition which is identified by a name and the two States from and to which an
* entity is being transitioned.
*
* <p>Currently, only exactly one toState and one fromState can be defined. However, it might be
* allowed in the future to have multiple states here.
*
* @author Timm Fitschen (t.fitschen@indiscale.com)
*/
public class Transition {
private String transitionIdVersion;
private State fromState;
private State toState;
private String name;
public Transition(EntityInterface transition) throws Message {
this.transitionIdVersion = transition.getIdVersion();
this.name = transition.getName();
this.fromState = getFromState(transition);
this.toState = getToState(transition);
}
private State getToState(EntityInterface transition) throws Message {
for (Property p : transition.getProperties()) {
if (p.getName().equals("to")) {
return createStateMessage(p).getState();
if (p.getName().equals(TO_STATE_PROPERTY_NAME)) {
return createState(p);
}
}
return null;
......@@ -49,8 +107,8 @@ public abstract class EntityStateJob extends EntityJob {
private State getFromState(EntityInterface transition) throws Message {
for (Property p : transition.getProperties()) {
if (p.getName().equals("from")) {
return createStateMessage(p).getState();
if (p.getName().equals(FROM_STATE_PROPERTY_NAME)) {
return createState(p);
}
}
return null;
......@@ -75,48 +133,167 @@ public abstract class EntityStateJob extends EntityJob {
@Override
public boolean equals(Object obj) {
if (obj instanceof Transition) {
return ((Transition) obj).transitionIdVersion.equals(this.transitionIdVersion);
Transition that = (Transition) obj;
return Objects.equals(this.getName(), that.getName())
&& Objects.equals(this.getFromState(), that.getFromState())
&& Objects.equals(this.getToState(), that.getToState());
}
return false;
}
public String getName() {
return this.name;
}
@Override
public int hashCode() {
return 1239141238 + this.transitionIdVersion.hashCode();
public String toString() {
return "Transition (name="
+ getName()
+ ", from="
+ getFromState().getStateName()
+ ", to="
+ getToState().getStateName()
+ ")";
}
}
/**
* The State instance represents a single entity state. This class is used for concrete state (the
* state of a stateful entity, say a Record) and abstract states (states which belong to a
* StateModel entity).
*
* <p>States are identified via their name and the name of the model to which they belong.
*
* <p>States are represented by Records with the state's name as the Record name. They belong to a
* StateModel iff the StateModel RecordType references the State Record. Each State should only
* belong to one StateModel.
*
* <p>Furthermore, States are the start or end point of Transitions which belong to the same
* StateModel. Each State can be part of several transitions at the same time.
*
* @author Timm Fitschen (t.fitschen@indiscale.com)
*/
public class State implements ToElementable {
private String stateIdVersion;
private String stateModelName = null;
private String stateName = null;
private EntityInterface stateEntity = null;
private EntityInterface stateModelEntity = null;
private StateModel stateModel;
public State(String stateName, String stateModelName) throws Message {
this.stateName = stateName;
this.stateModelName = stateModelName;
}
public State(EntityInterface stateEntity) {
this.stateIdVersion = stateEntity.getIdVersion();
public State(EntityInterface stateEntity, EntityInterface stateModelEntity) throws Message {
this.stateEntity = stateEntity;
this.stateName = stateEntity.getName();
this.stateModelEntity = stateModelEntity;
this.stateModelName = stateModelEntity.getName();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof State) {
return ((State) obj).stateIdVersion.equals(this.stateIdVersion);
State that = (State) obj;
return Objects.equals(that.getStateName(), this.getStateName())
&& Objects.equals(that.getStateModelName(), this.getStateModelName());
}
return false;
}
@Override
public int hashCode() {
return 1239141238 + this.stateIdVersion.hashCode();
return 21364234 + this.getStateName().hashCode() + this.getStateModelName().hashCode();
}
@Override
public void addToElement(Element ret) {
// TODO Auto-generated method stub
Element e = new Element(STATE_XML_TAG);
if (this.getStateModelName() != null) {
e.setAttribute(MODEL_ATTRIBUTE_NAME, this.getStateModelName());
}
if (this.getStateName() != null) {
e.setAttribute(STATE_ATTRIBUTE_NAME, this.getStateName());
}
ret.addContent(e);
}
private String getStateModelName() {
return this.stateModelName;
}
private String getStateName() {
return this.stateName;
}
public StateModel getStateModel() throws Message {
if (this.stateModel == null) {
this.stateModel = new StateModel(getStateModelEntity());
}
return this.stateModel;
}
public boolean isInitial() throws Message {
return Objects.equals(this, getStateModel().initialState);
}
public boolean isFinal() throws Message {
return Objects.equals(this, getStateModel().finalState);
}
/**
* Create a property which represents the current entity state of a stateful entity.
*
* @return stateProperty
* @throws Message
*/
public Property createStateProperty() throws Message {
EntityInterface stateRecordType = getStateRecordType();
Property stateProperty = new Property(stateRecordType);
stateProperty.setDatatype(new ReferenceDatatype2(stateRecordType));
stateProperty.setValue(new ReferenceValue(getStateEntity(), false));
stateProperty.setStatementStatus(StatementStatus.FIX);
return stateProperty;
}
public EntityInterface getStateEntity() throws Message {
if (this.stateEntity == null) {
this.stateEntity = retrieveStateEntity(getStateModelEntity(), this.getStateName());
}
return this.stateEntity;
}
public EntityInterface getStateModelEntity() throws Message {
if (this.stateModelEntity == null) {
this.stateModelEntity = retrieveStateModelEntity(this.getStateModelName());
}
return this.stateModelEntity;
}
@Override
public String toString() {
return "State (name=" + getStateName() + ", model=" + getStateModelName() + ")";
}
}
/**
* A StateModel is an abstract definition of a Finite State Machine for entities.
*
* <p>It consists of a set of States, a set of transitions, a initial state and a final state.
*
* <p>If the StateModel has no initial state, it cannot be initialized (no entity will ever be in
* any of the StateModel's states) without using the forceInitialState flag.
*
* <p>If the StateModel has not final state, an entity with any of the states from this StateModel
* cannot leave this StateModel (and cannot be deleted either) without using the forceFinalState
* flag.
*
* @author Timm Fitschen (t.fitschen@indiscale.com)
*/
public class StateModel {
private String stateModelIdVersion;
private String name;
private Set<State> states;
private Set<Transition> transitions;
......@@ -124,8 +301,7 @@ public abstract class EntityStateJob extends EntityJob {
private State finalState;
public StateModel(EntityInterface stateModelEntity) throws Message {
this.stateModelIdVersion = stateModelEntity.getIdVersion();
this.getName();
this.name = stateModelEntity.getName();
this.states = getStates(stateModelEntity);
this.transitions = getTransitions(stateModelEntity);
this.finalState = getFinalState(stateModelEntity);
......@@ -134,8 +310,8 @@ public abstract class EntityStateJob extends EntityJob {
private State getInitialState(EntityInterface stateModelEntity) throws Message {
for (Property p : stateModelEntity.getProperties()) {
if (p.getName().equals("initial")) {
return createStateMessage(p).getState();
if (p.getName().equals(INITIAL_STATE_PROPERTY_NAME)) {
return createState(p);
}
}
return null;
......@@ -143,8 +319,8 @@ public abstract class EntityStateJob extends EntityJob {
private State getFinalState(EntityInterface stateModelEntity) throws Message {
for (Property p : stateModelEntity.getProperties()) {
if (p.getName().equals("final")) {
return createStateMessage(p).getState();
if (p.getName().equals(FINAL_STATE_PROPERTY_NAME)) {
return createState(p);
}
}
return null;
......@@ -152,7 +328,7 @@ public abstract class EntityStateJob extends EntityJob {
private Set<Transition> getTransitions(EntityInterface stateModelEntity) throws Message {
for (Property p : stateModelEntity.getProperties()) {
if (p.getName().equals("Transition")) {
if (p.getName().equals(TRANSITION_RECORD_TYPE_NAME)) {
return createTransitions(p);
}
}
......@@ -175,7 +351,6 @@ public abstract class EntityStateJob extends EntityJob {
}
}
} catch (Exception e) {
e.printStackTrace();
throw COULD_NOT_GENERATE_TRANSITIONS;
}
return result;
......@@ -183,33 +358,32 @@ public abstract class EntityStateJob extends EntityJob {
private Set<State> getStates(EntityInterface stateModelEntity) throws Message {
for (Property p : stateModelEntity.getProperties()) {
if (p.getName().equals("State")) {
return createStates(p);
if (p.getName().equals(STATE_RECORD_TYPE_NAME)) {
return createStates(p, stateModelEntity);
}
}
return null;
}
private Set<State> createStates(Property p) throws Message {
Set<State> result = new HashSet<>();
try {
private Set<State> createStates(Property p, EntityInterface stateModelEntity) throws Message {
if (!(p.getDatatype() instanceof AbstractCollectionDatatype)) {
return result;
return null;
}
try {
Set<State> result = new HashSet<>();
p.parseValue();
CollectionValue vals = (CollectionValue) p.getValue();
for (IndexedSingleValue val : vals) {
if (val.getWrapped() instanceof ReferenceValue) {
Integer refid = ((ReferenceValue) val.getWrapped()).getId();
EntityInterface transition = retrieveValidEntity(refid);
result.add(new State(transition));
EntityInterface stateEntity = retrieveValidEntity(refid);
result.add(new State(stateEntity, stateModelEntity));
}
}
return result;
} catch (Exception e) {
e.printStackTrace();
throw COULD_NOT_GENERATE_STATES;
}
return result;
}
public String getName() {
......@@ -235,104 +409,12 @@ public abstract class EntityStateJob extends EntityJob {
@Override
public boolean equals(Object obj) {
if (obj instanceof StateModel) {
return ((StateModel) obj).stateModelIdVersion.equals(this.stateModelIdVersion);
return ((StateModel) obj).getName().equals(this.getName());
}
return false;
}
@Override
public int hashCode() {
return 1239141238 + this.stateModelIdVersion.hashCode();
}
}
public class StateMessage implements ToElementable {
String name;
String model;
private EntityInterface stateModelEntity;
private EntityInterface stateEntity;
/**
* @param name
* @param model
*/
public StateMessage(String name, String model) {
this.name = name;
this.model = model;
}
@Override
public void addToElement(Element ret) {
Element e = new Element("State");
e.setAttribute("model", this.model);
e.setAttribute("name", this.name);
ret.addContent(e);
}
public Property createStateProperty() {
EntityInterface stateRecordType = getStateRecordType();
Property stateProperty = new Property(stateRecordType);
stateProperty.setDatatype(new ReferenceDatatype2(stateRecordType));
stateProperty.setValue(new ReferenceValue(getStateEntity(), false));
stateProperty.setStatementStatus(StatementStatus.FIX);
return stateProperty;
}
private EntityInterface getStateRecordType() {
try {
return retrieveValidSparseEntityByName("State");
} catch (Message e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public EntityInterface getStateModelEntity() {
if (this.stateModelEntity == null) {
try {
this.stateModelEntity = retrieveStateModelEntity(model);
} catch (Message e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return this.stateModelEntity;
}
public EntityInterface getStateEntity() {
if (this.stateEntity == null) {
try {
this.stateEntity = retrieveStateEntity(getStateModelEntity(), this.name);
} catch (Message e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return this.stateEntity;
}
public StateModel getStateModel() throws Message {
return new StateModel(getStateModelEntity());
}
public State getState() {
return new State(getStateEntity());
}
}
private static final Message STATE_MODEL_NOT_FOUND =
new Message(MessageType.Error, "StateModel does not exist.");
private static final Message STATE_ENTITY_NOT_FOUND =
new Message(MessageType.Error, "State does not exist in this StateModel.");
private static final Message COULD_NOT_GENERATE_STATE_MESSAGE =
new Message(MessageType.Error, "Could not generate the state message");
private static final Message COULD_NOT_GENERATE_TRANSITIONS =
new Message(MessageType.Error, "Could not generate transitions");
private static final Message COULD_NOT_GENERATE_STATES =
new Message(MessageType.Error, "Could not generate states");
protected EntityInterface retrieveStateEntity(EntityInterface stateModelEntity, String stateName)
throws Message {
......@@ -340,10 +422,10 @@ public abstract class EntityStateJob extends EntityJob {
try {
list = execute(new GetIDByName(stateName)).getList();
} catch (EntityDoesNotExistException e) {
throw STATE_ENTITY_NOT_FOUND;
throw STATE_NOT_IN_STATE_MODEL;
}
for (Property p : stateModelEntity.getProperties()) {
if (Objects.equals(p.getName(), "State")) {
if (Objects.equals(p.getName(), STATE_RECORD_TYPE_NAME)) {
p.parseValue();
for (IndexedSingleValue val : ((CollectionValue) p.getValue())) {
ReferenceValue refid = (ReferenceValue) val.getWrapped();
......@@ -357,7 +439,7 @@ public abstract class EntityStateJob extends EntityJob {
}
}
}
throw STATE_ENTITY_NOT_FOUND;
throw STATE_NOT_IN_STATE_MODEL;
}
protected EntityInterface retrieveStateModelEntity(String stateModel) throws Message {
......@@ -368,99 +450,114 @@ public abstract class EntityStateJob extends EntityJob {
}
}
protected StateMessage getStateMessage() {
return getStateMessage(false);
protected EntityInterface getStateRecordType() throws Message {
// TODO this should be cached
return retrieveValidSparseEntityByName(STATE_RECORD_TYPE_NAME);
}
protected StateMessage getStateMessage(EntityInterface entity) {
return getStateMessage(entity, false);
protected State getState() {
return getState(false);
}
protected StateMessage getStateMessage(EntityInterface entity, boolean remove) {
protected State getState(EntityInterface entity) {
return getState(entity, false);
}
protected State getState(EntityInterface entity, boolean remove) {
Iterator<ToElementable> messages = entity.getMessages().iterator();
while (messages.hasNext()) {
ToElementable s = messages.next();
if (s instanceof StateMessage) {
if (s instanceof State) {
if (remove) {
messages.remove();
}
return (StateMessage) s;
return (State) s;
}
}
return null;
}
protected StateMessage getStateMessage(boolean remove) {
return getStateMessage(getEntity(), remove);
protected State getState(boolean remove) {
return getState(getEntity(), remove);
}
protected Property getStateProperty(EntityInterface entity, boolean remove) {
protected List<Property> getStateProperties(EntityInterface entity, boolean remove) {
Iterator<Property> it = entity.getProperties().iterator();
List<Property> result = new ArrayList<>();
while (it.hasNext()) {
Property p = it.next();
if (Objects.equals(p.getName(), "State")) {
if (Objects.equals(p.getName(), STATE_RECORD_TYPE_NAME)) {
if (!(p.getDatatype() instanceof ReferenceDatatype)) {
continue;
}
if (remove) {
it.remove();
}
return p;
result.add(p);
}
}
return null;
return result;
}
protected Property getStateProperty(boolean remove) {
return getStateProperty(getEntity(), remove);
protected List<Property> getStateProperties(boolean remove) {
return getStateProperties(getEntity(), remove);
}
protected ClientMessage getStateClientMessage(EntityInterface entity, boolean remove) {
Iterator<Message> stateMessages = entity.getMessages("State").iterator();
protected List<ClientMessage> getStateClientMessages(EntityInterface entity, boolean remove) {
Iterator<Message> stateMessages = entity.getMessages(STATE_XML_TAG).iterator();
List<ClientMessage> result = new ArrayList<>();
while (stateMessages.hasNext()) {
Message s = stateMessages.next();
if (s instanceof ClientMessage) {
if (remove) {
stateMessages.remove();
}
return (ClientMessage) s;
result.add((ClientMessage) s);
}
}
return null;
return result;
}
protected ClientMessage getStateClientMessage(boolean remove) {
return getStateClientMessage(getEntity(), remove);
protected List<ClientMessage> getStateClientMessages(boolean remove) {
return getStateClientMessages(getEntity(), remove);
}
protected StateMessage createStateMessage(ClientMessage s) throws Message {
String stateModel = s.getProperty("model");
String stateName = s.getProperty("name");
return new StateMessage(stateName, stateModel);
protected State createState(ClientMessage s) throws Message {
String stateModel = s.getProperty(MODEL_ATTRIBUTE_NAME);
if (stateModel == null) {
throw STATE_MODEL_NOT_SPECIFIED;
}
String stateName = s.getProperty(STATE_ATTRIBUTE_NAME);
if (stateName == null) {
throw STATE_NOT_SPECIFIED;
}
return new State(stateName, stateModel);
}
protected StateMessage createStateMessage(Property p) throws Message {
protected State createState(Property p) throws Message {
try {
p.parseValue();
ReferenceValue refid = (ReferenceValue) p.getValue();
EntityInterface stateEntity = retrieveValidSparseEntityById(refid.getId(), null);
EntityInterface stateModelEntity = findStateModel(stateEntity);
return new StateMessage(stateEntity.getName(), stateModelEntity.getName());
return new State(stateEntity, stateModelEntity);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw COULD_NOT_GENERATE_STATE_MESSAGE;
}
}
EntityInterface findStateModel(EntityInterface stateEntity) throws Exception {
// TODO this should be cached
Query query =
new Query(
"FIND Record StateModel WHICH REFERENCES " + Integer.toString(stateEntity.getId()),
"FIND RECORD "
+ STATE_MODEL_RECORD_TYPE_NAME
+ " WHICH REFERENCES "
+ Integer.toString(stateEntity.getId()),
getUser(),
null);
query.execute(getTransaction().getAccess());
return retrieveValidSparseEntityById(query.getResultSet().get(0), null);
return retrieveValidEntity(query.getResultSet().get(0));
}
}
package org.caosdb.server.jobs.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.caosdb.server.entity.ClientMessage;
import org.caosdb.server.entity.Entity;
......@@ -24,10 +26,23 @@ public class InitEntityState extends EntityStateJob implements Observer {
@Override
protected void run() {
try {
State newState = initStateMessage(getEntity());
State newState = null;
{
List<State> states = initStateMessage(getEntity());
if (states.size() > 1) {
throw new Message(
MessageType.Error, "Currently, each entity can only have one state at a time.");
} else if (states.size() == 1) {
newState = states.get(0);
}
}
try {
if (getEntity() instanceof UpdateEntity) {
State oldState = initStateMessage(((UpdateEntity) getEntity()).getOriginal());
List<State> states = initStateMessage(((UpdateEntity) getEntity()).getOriginal());
State oldState = null;
if (states.size() == 1) {
oldState = states.get(0);
}
if (!Objects.equals(newState, oldState)) {
getEntity().acceptObserver(this);
}
......@@ -46,26 +61,33 @@ public class InitEntityState extends EntityStateJob implements Observer {
MessageType.Warning, "State error in previous entity version\n" + m.getDescription());
}
private State initStateMessage(EntityInterface entity) throws Message {
ClientMessage stateClientMessage = getStateClientMessage(entity, true);
if (stateClientMessage != null) {
StateMessage stateMessage = createStateMessage(stateClientMessage);
private List<State> initStateMessage(EntityInterface entity) throws Message {
List<ClientMessage> stateClientMessages = getStateClientMessages(entity, true);
List<State> result = new ArrayList<>();
if (stateClientMessages != null) {
for (ClientMessage s : stateClientMessages) {
State stateMessage = createState(s);
entity.addMessage(stateMessage);
return stateMessage.getState();
result.add(stateMessage);
}
Property stateProperty = getStateProperty(entity, true);
if (stateProperty != null) {
StateMessage stateMessage = createStateMessage(stateProperty);
}
List<Property> stateProperties = getStateProperties(entity, true);
if (stateProperties != null) {
for (Property p : stateProperties) {
State stateMessage = createState(p);
entity.addMessage(stateMessage);
return stateMessage.getState();
result.add(stateMessage);
}
}
return null;
return result;
}
@Override
public boolean notifyObserver(String e, Observable o) {
if (e == Entity.ENTITY_STATUS_CHANGED_EVENT) {
if (o == getEntity() && getEntity().getEntityStatus() == EntityStatus.VALID) {
// The Update.deriveUpdate method didn't recognize that the state is changing and set the
// entity to "VALID"
getEntity().setEntityStatus(EntityStatus.QUALIFIED);
return false;
}
......
package org.caosdb.server.jobs.core;
import java.util.List;
import org.caosdb.server.entity.Message;
import org.caosdb.server.entity.wrapper.Property;
import org.caosdb.server.jobs.JobAnnotation;
import org.caosdb.server.jobs.JobExecutionTime;
import org.caosdb.server.transaction.Retrieve;
@JobAnnotation(loadAlways = true, time = JobExecutionTime.POST_TRANSACTION)
public class WriteStateMessage extends EntityStateJob {
public class MakeStateMessage extends EntityStateJob {
@Override
protected void run() {
try {
Property stateProperty = getStateProperty(true);
if (stateProperty != null) {
StateMessage stateMessage = createStateMessage(stateProperty);
List<Property> stateProperties = getStateProperties(true);
// only add the State during Retrieve. In all other cases, the State is already present.
if (stateProperties != null && getTransaction() instanceof Retrieve) {
for (Property s : stateProperties) {
State stateMessage = createState(s);
getEntity().addMessage(stateMessage);
}
}
} catch (Message e) {
getEntity().addError(e);
}
......
package org.caosdb.server.jobs.core;
import org.caosdb.server.entity.Message;
import org.caosdb.server.jobs.JobAnnotation;
import org.caosdb.server.jobs.JobExecutionTime;
import org.caosdb.server.transaction.WriteTransaction;
......@@ -8,15 +9,21 @@ import org.caosdb.server.transaction.WriteTransaction;
loadAlways = true,
transaction = WriteTransaction.class,
time = JobExecutionTime.PRE_TRANSACTION)
public class ParseStateMessage extends EntityStateJob {
public class MakeStateProperty extends EntityStateJob {
@Override
protected void run() {
StateMessage s = getStateMessage(true);
if (s != null) addStateProperty(s);
State s = getState();
if (s != null) {
try {
addStateProperty(s);
} catch (Message e) {
getEntity().addError(e);
}
}
}
private void addStateProperty(StateMessage stateEntity) {
private void addStateProperty(State stateEntity) throws Message {
getEntity().addProperty(stateEntity.createStateProperty());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment