diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/GetIDByName.java b/src/main/java/org/caosdb/server/database/backend/transaction/GetIDByName.java index b43db0010eeeb3179bb91333d854872f5e8ab18a..8fffb415bdb5ebeb5fb17b3603312bbfcab1551e 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/GetIDByName.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/GetIDByName.java @@ -28,6 +28,7 @@ import org.caosdb.server.database.backend.interfaces.GetIDByNameImpl; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.database.exceptions.TransactionException; +import org.caosdb.server.query.Query.Role; public class GetIDByName extends BackendTransaction { @@ -37,11 +38,11 @@ public class GetIDByName extends BackendTransaction { private final String role; public GetIDByName(final String name) { - this(name, null, true); + this(name, (String) null, true); } public GetIDByName(final String name, final boolean unique) { - this(name, null, unique); + this(name, (String) null, unique); } public GetIDByName(final String name, final String role) { @@ -54,6 +55,10 @@ public class GetIDByName extends BackendTransaction { this.unique = unique; } + public GetIDByName(String name, Role role, boolean unique) { + this(name, role.toString(), unique); + } + @Override public void execute() throws TransactionException { final GetIDByNameImpl t = getImplementation(GetIDByNameImpl.class); diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java index bd5bf63a5eb7b194e119c56293e756ff0c4b6a20..7cb0cb40f84e13107feef6d969718d99fda70530 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityProperties.java @@ -167,7 +167,11 @@ public class InsertEntityProperties extends BackendTransaction { fp.collection = ((AbstractCollectionDatatype) property.getDatatype()).getCollectionName(); } else { - fp.type = property.getDatatype().getId().toString(); + try { + fp.type = property.getDatatype().getId().toString(); + } catch (NullPointerException e) { + throw e; + } } } } diff --git a/src/main/java/org/caosdb/server/entity/ClientMessage.java b/src/main/java/org/caosdb/server/entity/ClientMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..22f257da013541584ad592d717fe7f5663f3baea --- /dev/null +++ b/src/main/java/org/caosdb/server/entity/ClientMessage.java @@ -0,0 +1,44 @@ +package org.caosdb.server.entity; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import org.jdom2.Attribute; +import org.jdom2.Element; + +public class ClientMessage extends Message { + + private static final long serialVersionUID = 1L; + private Map<String, String> properties = new HashMap<>(); + + public ClientMessage(String type) { + super(type, null, null, null); + } + + @Override + public Element toElement() { + final Element e = new Element(this.type); + for (Entry<String, String> a : this.properties.entrySet()) { + e.setAttribute(a.getKey(), a.getValue()); + } + return e; + } + + @Override + public void addToElement(final Element parent) { + final Element e = toElement(); + parent.addContent(e); + } + + public static ClientMessage fromXML(Element pe) { + ClientMessage result = new ClientMessage(pe.getName()); + for (Attribute a : pe.getAttributes()) { + result.properties.put(a.getName(), a.getValue()); + } + return result; + } + + public String getProperty(String key) { + return properties.get(key); + } +} diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java index 8578191c40c229ae1b9f0e873d35690c55cf2bfc..27570ec81636c7bb9a20bde04fb68f07b4956fea 100644 --- a/src/main/java/org/caosdb/server/entity/Entity.java +++ b/src/main/java/org/caosdb/server/entity/Entity.java @@ -848,34 +848,7 @@ public class Entity extends AbstractObservable implements EntityInterface { } else if (getRole() == Role.QueryTemplate && pe.getName().equalsIgnoreCase("Query")) { setQueryTemplateDefinition(pe.getTextNormalize()); } else { - final String type = pe.getName(); - Integer code = null; - String localDescription = null; - String body = null; - - // Parse MESSAGE CODE. - if (pe.getAttribute("code") != null && !pe.getAttributeValue("code").equals("")) { - try { - code = Integer.parseInt(pe.getAttributeValue("code")); - } catch (final NumberFormatException e) { - addInfo("Message code was " + pe.getAttributeValue("code") + "."); - addError(ServerMessages.PARSING_FAILED); - setEntityStatus(EntityStatus.UNQUALIFIED); - } - } - - // Parse MESSAGE DESCRIPTION. - if (pe.getAttribute("description") != null - && !pe.getAttributeValue("description").equals("")) { - localDescription = pe.getAttributeValue("description"); - } - - // Parse MESSAGE BODY. - if (pe.getTextTrim() != null && !pe.getTextTrim().equals("")) { - body = pe.getTextTrim(); - } - - addMessage(new Message(type, code, localDescription, body)); + addMessage(ClientMessage.fromXML(pe)); } } diff --git a/src/main/java/org/caosdb/server/entity/Message.java b/src/main/java/org/caosdb/server/entity/Message.java index 743a2b62fa87570e0dbca05a1f279e6764920f0b..3e469b2dc84b07e3ac3de378f8990cc520989cc0 100644 --- a/src/main/java/org/caosdb/server/entity/Message.java +++ b/src/main/java/org/caosdb/server/entity/Message.java @@ -27,7 +27,7 @@ import org.jdom2.Element; public class Message extends Exception implements Comparable<Message>, ToElementable { - private final String type; + protected final String type; private final Integer code; private final String description; private final String body; @@ -127,7 +127,7 @@ public class Message extends Exception implements Comparable<Message>, ToElement return this.code; } - public final Element toElement() { + public Element toElement() { final Element e = new Element(this.type); if (this.code != null) { e.setAttribute("code", Integer.toString(this.code)); @@ -142,7 +142,7 @@ public class Message extends Exception implements Comparable<Message>, ToElement } @Override - public final void addToElement(final Element parent) { + public void addToElement(final Element parent) { final Element e = toElement(); parent.addContent(e); } diff --git a/src/main/java/org/caosdb/server/entity/UpdateEntity.java b/src/main/java/org/caosdb/server/entity/UpdateEntity.java index aa6d591602df66cc9317351cf6a1ea980bc53ea3..1c63e4fb8f42faea2c0ea101870ba1c089e3e17a 100644 --- a/src/main/java/org/caosdb/server/entity/UpdateEntity.java +++ b/src/main/java/org/caosdb/server/entity/UpdateEntity.java @@ -27,6 +27,8 @@ import org.jdom2.Element; public class UpdateEntity extends WritableEntity { + private EntityInterface original = null; + public UpdateEntity(final Element element) { super(element); } @@ -35,4 +37,12 @@ public class UpdateEntity extends WritableEntity { public boolean skipJob() { return getEntityStatus() != EntityStatus.QUALIFIED; } + + public void setOriginal(EntityInterface original) { + this.original = original; + } + + public EntityInterface getOriginal() { + return this.original; + } } diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java index d1e3fef39fcd6a2fb5175e4dd396686bcf773b4e..76e6ba5d4e3c2b02299715cae681c48083ff97c4 100644 --- a/src/main/java/org/caosdb/server/jobs/Job.java +++ b/src/main/java/org/caosdb/server/jobs/Job.java @@ -50,14 +50,11 @@ import org.caosdb.server.entity.Message; import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.jobs.core.Mode; import org.caosdb.server.transaction.Transaction; -import org.caosdb.server.utils.AbstractObservable; import org.caosdb.server.utils.EntityStatus; -import org.caosdb.server.utils.Observable; -import org.caosdb.server.utils.Observer; import org.caosdb.server.utils.ServerMessages; import org.reflections.Reflections; -public abstract class Job extends AbstractObservable implements Observer { +public abstract class Job { private Transaction<? extends TransactionContainer> transaction = null; private Mode mode = null; @@ -77,7 +74,7 @@ public abstract class Job extends AbstractObservable implements Observer { return getContainer().getRequestId(); } - protected Subject getUser() { + public Subject getUser() { return getTransaction().getTransactor(); } @@ -134,10 +131,6 @@ public abstract class Job extends AbstractObservable implements Observer { getTransaction().getSchedule().runJob(entity, jobclass); } - protected void runJobFromSchedule(ScheduledJob job) { - getTransaction().getSchedule().runJob(job); - } - public EntityInterface getEntity() { return this.entity; } @@ -176,8 +169,8 @@ public abstract class Job extends AbstractObservable implements Observer { return retrieveValidSparseEntityById(retrieveValidIDByName(name), null); } - protected final EntityInterface retrieveValidSparseEntityById( - final Integer id, final String version) throws Message { + public final EntityInterface retrieveValidSparseEntityById(final Integer id, final String version) + throws Message { String resulting_version = version; if (version == null || version.equals("HEAD")) { @@ -212,11 +205,11 @@ public abstract class Job extends AbstractObservable implements Observer { return ret; } - protected final EntityInterface retrieveValidEntity(Integer id) { + public final EntityInterface retrieveValidEntity(Integer id) { return execute(new RetrieveFullEntity(id)).getContainer().get(0); } - protected final Integer retrieveValidIDByName(final String name) { + public final Integer retrieveValidIDByName(final String name) { return execute(new GetIDByName(name)).getId(); } @@ -232,13 +225,13 @@ public abstract class Job extends AbstractObservable implements Observer { } } - @Override - public boolean notifyObserver(final String e, final Observable o) { - if (getEntity().getEntityStatus() != EntityStatus.UNQUALIFIED) { - getTransaction().getSchedule().runJob(this); - } - return true; - } + // @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; @@ -458,10 +451,6 @@ public abstract class Job extends AbstractObservable implements Observer { + "]"; } - public void finish() { - super.removeAllObservers(); - } - public void print() { System.out.println(toString()); } diff --git a/src/main/java/org/caosdb/server/jobs/Schedule.java b/src/main/java/org/caosdb/server/jobs/Schedule.java index 7f35106bcdda8ddc00512197091fa096a6bc157e..77bc57c9e3f4183bf572b6a95dfe16fc79b6e003 100644 --- a/src/main/java/org/caosdb/server/jobs/Schedule.java +++ b/src/main/java/org/caosdb/server/jobs/Schedule.java @@ -22,94 +22,48 @@ */ package org.caosdb.server.jobs; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import org.caosdb.server.entity.EntityInterface; -class ScheduledJob { - - long runtime = 0; - final Job job; - private long startTime = -1; - - public ScheduledJob(final Job j) { - this.job = j; - } - - public void run() { - if (!hasStarted()) { - start(); - this.job.run(); - finish(); - - this.job.notifyObservers(null); - } - } - - private void start() { - this.startTime = System.currentTimeMillis(); - } - - private void finish() { - this.runtime += System.currentTimeMillis() - this.startTime; - this.job - .getContainer() - .getTransactionBenchmark() - .addMeasurement(this.job.getClass().getSimpleName(), this.runtime); - } - - void pause() { - this.runtime += System.currentTimeMillis() - this.startTime; - } - - void unpause() { - start(); - } - - private boolean hasStarted() { - return this.startTime != -1; - } - - public JobExecutionTime getExecutionTime() { - return this.job.getExecutionTime(); - } - - public boolean skip() { - return this.job.getTarget().skipJob(); - } -} - public class Schedule { - private final CopyOnWriteArrayList<ScheduledJob> jobs = new CopyOnWriteArrayList<ScheduledJob>(); + private final Map<Integer, List<ScheduledJob>> jobLists = new HashMap<>(); private ScheduledJob running = null; - public void addAll(final Collection<Job> jobs) { + public List<ScheduledJob> addAll(final Collection<Job> jobs) { + List<ScheduledJob> result = new ArrayList<ScheduledJob>(jobs.size()); for (final Job j : jobs) { - add(j); + result.add(add(j)); } + return result; } public ScheduledJob add(final Job j) { ScheduledJob ret = new ScheduledJob(j); - this.jobs.add(ret); + List<ScheduledJob> jobs = jobLists.get(ret.getExecutionTime().ordinal()); + if (jobs == null) { + jobs = new CopyOnWriteArrayList<ScheduledJob>(); + jobLists.put(ret.getExecutionTime().ordinal(), jobs); + } + jobs.add(ret); return ret; } public void runJobs(final JobExecutionTime time) { - for (final ScheduledJob scheduledJob : this.jobs) { - if (scheduledJob.getExecutionTime().ordinal() == time.ordinal() - || (time.ordinal() <= JobExecutionTime.POST_CHECK.ordinal() - && scheduledJob.getExecutionTime().ordinal() < time.ordinal())) { + List<ScheduledJob> jobs = this.jobLists.get(time.ordinal()); + if (jobs != null) { + for (final ScheduledJob scheduledJob : jobs) { runJob(scheduledJob); } } } - protected void runJob(final ScheduledJob scheduledJob) { - if (!this.jobs.contains(scheduledJob)) { - throw new RuntimeException("Job was not in schedule."); - } + public void runJob(final ScheduledJob scheduledJob) { if (scheduledJob.skip()) { return; } @@ -127,18 +81,12 @@ public class Schedule { } } - public void runJob(final Job j) { - for (final ScheduledJob scheduledJob : this.jobs) { - if (scheduledJob.job == j) { - scheduledJob.run(); - return; - } - } - throw new RuntimeException("Job was not in schedule."); - } - public void runJob(final EntityInterface entity, final Class<? extends Job> jobclass) { - for (final ScheduledJob scheduledJob : this.jobs) { + List<ScheduledJob> jobs = + jobclass.isAnnotationPresent(JobAnnotation.class) + ? this.jobLists.get(jobclass.getAnnotation(JobAnnotation.class).time().ordinal()) + : this.jobLists.get(JobExecutionTime.CHECK.ordinal()); + for (final ScheduledJob scheduledJob : jobs) { if (jobclass.isInstance(scheduledJob.job)) { if (scheduledJob.job.getEntity() == entity) { runJob(scheduledJob); diff --git a/src/main/java/org/caosdb/server/jobs/ScheduledJob.java b/src/main/java/org/caosdb/server/jobs/ScheduledJob.java new file mode 100644 index 0000000000000000000000000000000000000000..94aebf1510b4c9657b3a2c172e734b6a76a54e09 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/ScheduledJob.java @@ -0,0 +1,57 @@ +package org.caosdb.server.jobs; + +public class ScheduledJob { + + long runtime = 0; + final Job job; + private long startTime = -1; + + ScheduledJob(final Job j) { + this.job = j; + } + + void run() { + if (!hasStarted()) { + start(); + this.job.run(); + finish(); + } + } + + @Override + public String toString() { + return this.job.toString(); + } + + private void start() { + this.startTime = System.currentTimeMillis(); + } + + private void finish() { + this.runtime += System.currentTimeMillis() - this.startTime; + this.job + .getContainer() + .getTransactionBenchmark() + .addMeasurement(this.job.getClass().getSimpleName(), this.runtime); + } + + void pause() { + this.runtime += System.currentTimeMillis() - this.startTime; + } + + void unpause() { + start(); + } + + private boolean hasStarted() { + return this.startTime != -1; + } + + public JobExecutionTime getExecutionTime() { + return this.job.getExecutionTime(); + } + + public boolean skip() { + return this.job.getTarget().skipJob(); + } +} diff --git a/src/main/java/org/caosdb/server/jobs/core/Atomic.java b/src/main/java/org/caosdb/server/jobs/core/Atomic.java index 509f87fc8515ca08daa1a5cb8ef6571dfe6fa441..45b5de8e4d5835bd835a797290e2d3c875687e44 100644 --- a/src/main/java/org/caosdb/server/jobs/core/Atomic.java +++ b/src/main/java/org/caosdb/server/jobs/core/Atomic.java @@ -30,13 +30,14 @@ import org.caosdb.server.jobs.JobExecutionTime; import org.caosdb.server.transaction.WriteTransaction; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.Observable; +import org.caosdb.server.utils.Observer; import org.caosdb.server.utils.ServerMessages; @JobAnnotation( - time = JobExecutionTime.POST_CHECK, + time = JobExecutionTime.PRE_TRANSACTION, transaction = WriteTransaction.class, loadAlways = true) -public class Atomic extends ContainerJob { +public class Atomic extends ContainerJob implements Observer { private boolean doCheck() { if (getContainer().getStatus() == EntityStatus.QUALIFIED) { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java index ce9943ecbefb4d669003a097e74bf9baff078167..f46013d278f268b95b1b105130e1eabd74f3a9f4 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java @@ -33,6 +33,7 @@ import org.caosdb.server.entity.Message; import org.caosdb.server.entity.Role; import org.caosdb.server.jobs.EntityJob; import org.caosdb.server.jobs.Job; +import org.caosdb.server.jobs.ScheduledJob; import org.caosdb.server.permissions.EntityPermission; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; @@ -81,8 +82,8 @@ public final class CheckDatatypePresent extends EntityJob { // run jobsreturn this.entities; final List<Job> datatypeJobs = loadDataTypeSpecificJobs(); - getTransaction().getSchedule().addAll(datatypeJobs); - for (final Job job : datatypeJobs) { + List<ScheduledJob> scheduledJobs = getTransaction().getSchedule().addAll(datatypeJobs); + for (final ScheduledJob job : scheduledJobs) { getTransaction().getSchedule().runJob(job); } } else { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java index c6d10a8f4f7cd0656f571a4cbf70385ab9cb7725..02cb96fc1214712c7a8ef1bb98327ba9fb39e9d0 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java @@ -32,6 +32,8 @@ import org.caosdb.server.entity.Role; import org.caosdb.server.entity.wrapper.Parent; import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.jobs.EntityJob; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.JobExecutionTime; import org.caosdb.server.permissions.EntityPermission; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; @@ -41,6 +43,7 @@ import org.caosdb.server.utils.ServerMessages; * * @author tf */ +@JobAnnotation(time = JobExecutionTime.PRE_CHECK) public class CheckParValid extends EntityJob { @Override public final void run() { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java index 10ce6295a5b4910084d769278ca6348287e6223a..f95104bd2f2552a642e7e4bcd2fc13f7fd2788d4 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java @@ -31,6 +31,8 @@ import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.Message; import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.jobs.EntityJob; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.JobExecutionTime; import org.caosdb.server.permissions.EntityPermission; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; @@ -40,6 +42,7 @@ import org.caosdb.server.utils.ServerMessages; * * @author tf */ +@JobAnnotation(time = JobExecutionTime.PRE_CHECK) public class CheckPropValid extends EntityJob { @Override public final void run() { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java b/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java index 59fcfe1d877ca61c98674ee85919f03b7bd4278e..b9f804f519948b605a9fa8820c91495618463fba 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java @@ -37,6 +37,7 @@ import org.caosdb.server.entity.wrapper.Parent; import org.caosdb.server.jobs.EntityJob; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.Observable; +import org.caosdb.server.utils.Observer; import org.caosdb.server.utils.ServerMessages; /** @@ -45,7 +46,7 @@ import org.caosdb.server.utils.ServerMessages; * * @author tf */ -public class CheckRefidIsaParRefid extends EntityJob { +public class CheckRefidIsaParRefid extends EntityJob implements Observer { private void doJob() { try { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java index 42476f87b43496e162b6eb036480b6b745ee91f2..38320291d0d882e6eb8eb2eb54f9ba0e31c64dda 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java @@ -38,6 +38,7 @@ import org.caosdb.server.jobs.EntityJob; import org.caosdb.server.permissions.EntityPermission; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.Observable; +import org.caosdb.server.utils.Observer; import org.caosdb.server.utils.ServerMessages; /** @@ -45,7 +46,7 @@ import org.caosdb.server.utils.ServerMessages; * * @author tf */ -public class CheckRefidValid extends EntityJob { +public class CheckRefidValid extends EntityJob implements Observer { @Override public final void run() { try { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java new file mode 100644 index 0000000000000000000000000000000000000000..65df0fefa425617de6fcaf568782a8922a186b31 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java @@ -0,0 +1,81 @@ +package org.caosdb.server.jobs.core; + +import org.caosdb.server.entity.DeleteEntity; +import org.caosdb.server.entity.Message; +import org.caosdb.server.entity.Message.MessageType; +import org.caosdb.server.entity.UpdateEntity; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.JobExecutionTime; +import org.caosdb.server.transaction.WriteTransaction; + +@JobAnnotation( + time = JobExecutionTime.CHECK, + transaction = WriteTransaction.class, + loadAlways = true) +public class CheckStateTransition extends EntityStateJob { + + private static final Message TRANSITION_NOT_ALLOWED = + new Message(MessageType.Error, "Transition not allowed."); + + @Override + protected void run() { + try { + if (getEntity() instanceof UpdateEntity) { + StateMessage toState = getStateMessage(); + StateMessage fromState = getStateMessage(((UpdateEntity) getEntity()).getOriginal()); + checkStateTransition(fromState, toState); + } else if (getEntity() instanceof DeleteEntity) { + StateMessage finalState = getStateMessage(); + checkFinalState(finalState); + } else { + StateMessage initialState = getStateMessage(); + checkInitialState(initialState); + } + } catch (Message m) { + getEntity().addError(m); + } + } + + /** Check if state is valid and transition is allowed */ + private void checkStateTransition(StateMessage fromState, StateMessage toState) throws Message { + if (fromState == null && toState == null) { + return; + } else if (fromState == null && toState != null) { + checkInitialState(toState); + return; + } else if (toState == null && fromState != null) { + checkFinalState(fromState); + return; + } + + StateModel stateModel = findMatchingStateModel(fromState, toState); + if (stateModel == null) { + checkInitialState(toState); + checkFinalState(fromState); + return; + } + + for (Transition t : stateModel.getTransitions()) { + if (t.fromStatesInclude(fromState.getState()) && t.toStatesInclude(toState.getState())) { + return; + } + } + throw TRANSITION_NOT_ALLOWED; + } + + private StateModel findMatchingStateModel(StateMessage fromState, StateMessage 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 checkInitialState(StateMessage toState) { + // TODO Auto-generated method stub + } +} diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckValueParsable.java b/src/main/java/org/caosdb/server/jobs/core/CheckValueParsable.java index 34e1960961f72118567653a77e4f48fe357dfedc..1dd457143f8cc23c0136f2c51cb40edf8552ba15 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckValueParsable.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckValueParsable.java @@ -28,6 +28,7 @@ import org.caosdb.server.entity.Message; import org.caosdb.server.jobs.EntityJob; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.Observable; +import org.caosdb.server.utils.Observer; /** * Check whether the value of an entity is parsable according to the entity's data type. This job @@ -37,7 +38,7 @@ import org.caosdb.server.utils.Observable; * * @author tf */ -public class CheckValueParsable extends EntityJob { +public class CheckValueParsable extends EntityJob implements Observer { @Override public final void run() { @@ -96,7 +97,6 @@ public class CheckValueParsable extends EntityJob { // Therefore, not the whole test has to be run again. if (getEntity().hasDatatype()) { parseValue(); - super.notifyObservers(e); return false; } } diff --git a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java new file mode 100644 index 0000000000000000000000000000000000000000..32f1e0193102430e4923c96c9ceb7fa2099b52da --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java @@ -0,0 +1,466 @@ +package org.caosdb.server.jobs.core; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import org.caosdb.server.database.backend.transaction.GetIDByName; +import org.caosdb.server.database.exceptions.EntityDoesNotExistException; +import org.caosdb.server.datatype.AbstractCollectionDatatype; +import org.caosdb.server.datatype.CollectionValue; +import org.caosdb.server.datatype.IndexedSingleValue; +import org.caosdb.server.datatype.ReferenceDatatype; +import org.caosdb.server.datatype.ReferenceDatatype2; +import org.caosdb.server.datatype.ReferenceValue; +import org.caosdb.server.entity.ClientMessage; +import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.Message; +import org.caosdb.server.entity.Message.MessageType; +import org.caosdb.server.entity.StatementStatus; +import org.caosdb.server.entity.wrapper.Property; +import org.caosdb.server.entity.xml.ToElementable; +import org.caosdb.server.jobs.EntityJob; +import org.caosdb.server.query.Query; +import org.jdom2.Element; + +public abstract class EntityStateJob extends EntityJob { + + public class Transition { + + private String transitionIdVersion; + private State fromState; + private State toState; + + public Transition(EntityInterface transition) throws Message { + this.transitionIdVersion = transition.getIdVersion(); + 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(); + } + } + return null; + } + + private State getFromState(EntityInterface transition) throws Message { + for (Property p : transition.getProperties()) { + if (p.getName().equals("from")) { + return createStateMessage(p).getState(); + } + } + return null; + } + + public boolean fromStatesInclude(State previousState) { + return this.fromState.equals(previousState); + } + + public boolean toStatesInclude(State nextState) { + return this.toState.equals(nextState); + } + + public State getToState() { + return this.toState; + } + + public State getFromState() { + return this.fromState; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Transition) { + return ((Transition) obj).transitionIdVersion.equals(this.transitionIdVersion); + } + return false; + } + + @Override + public int hashCode() { + return 1239141238 + this.transitionIdVersion.hashCode(); + } + } + + public class State implements ToElementable { + + private String stateIdVersion; + + public State(EntityInterface stateEntity) { + this.stateIdVersion = stateEntity.getIdVersion(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof State) { + return ((State) obj).stateIdVersion.equals(this.stateIdVersion); + } + return false; + } + + @Override + public int hashCode() { + return 1239141238 + this.stateIdVersion.hashCode(); + } + + @Override + public void addToElement(Element ret) { + // TODO Auto-generated method stub + + } + } + + public class StateModel { + + private String stateModelIdVersion; + private String name; + private Set<State> states; + private Set<Transition> transitions; + private State initialState; + private State finalState; + + public StateModel(EntityInterface stateModelEntity) throws Message { + this.stateModelIdVersion = stateModelEntity.getIdVersion(); + this.getName(); + this.states = getStates(stateModelEntity); + this.transitions = getTransitions(stateModelEntity); + this.finalState = getFinalState(stateModelEntity); + this.initialState = getInitialState(stateModelEntity); + } + + private State getInitialState(EntityInterface stateModelEntity) throws Message { + for (Property p : stateModelEntity.getProperties()) { + if (p.getName().equals("initial")) { + return createStateMessage(p).getState(); + } + } + return null; + } + + private State getFinalState(EntityInterface stateModelEntity) throws Message { + for (Property p : stateModelEntity.getProperties()) { + if (p.getName().equals("final")) { + return createStateMessage(p).getState(); + } + } + return null; + } + + private Set<Transition> getTransitions(EntityInterface stateModelEntity) throws Message { + for (Property p : stateModelEntity.getProperties()) { + if (p.getName().equals("Transition")) { + return createTransitions(p); + } + } + return null; + } + + private Set<Transition> createTransitions(Property p) throws Message { + Set<Transition> result = new HashSet<>(); + try { + if (!(p.getDatatype() instanceof AbstractCollectionDatatype)) { + return result; + } + 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 Transition(transition)); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw COULD_NOT_GENERATE_TRANSITIONS; + } + return result; + } + + private Set<State> getStates(EntityInterface stateModelEntity) throws Message { + for (Property p : stateModelEntity.getProperties()) { + if (p.getName().equals("State")) { + return createStates(p); + } + } + return null; + } + + private Set<State> createStates(Property p) throws Message { + Set<State> result = new HashSet<>(); + try { + if (!(p.getDatatype() instanceof AbstractCollectionDatatype)) { + return result; + } + 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)); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw COULD_NOT_GENERATE_STATES; + } + return result; + } + + public String getName() { + return this.name; + } + + public Set<State> getStates() { + return this.states; + } + + public Set<Transition> getTransitions() { + return this.transitions; + } + + public State getFinalState() { + return this.finalState; + } + + public State getInitialState() { + return this.initialState; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof StateModel) { + return ((StateModel) obj).stateModelIdVersion.equals(this.stateModelIdVersion); + } + 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 { + + List<Integer> list; + try { + list = execute(new GetIDByName(stateName)).getList(); + } catch (EntityDoesNotExistException e) { + throw STATE_ENTITY_NOT_FOUND; + } + for (Property p : stateModelEntity.getProperties()) { + if (Objects.equals(p.getName(), "State")) { + p.parseValue(); + for (IndexedSingleValue val : ((CollectionValue) p.getValue())) { + ReferenceValue refid = (ReferenceValue) val.getWrapped(); + + // match id + for (Integer id : list) { + if (Objects.equals(refid.getId(), id)) { + return retrieveValidEntity(id); + } + } + } + } + } + throw STATE_ENTITY_NOT_FOUND; + } + + protected EntityInterface retrieveStateModelEntity(String stateModel) throws Message { + try { + return retrieveValidEntity(retrieveValidIDByName(stateModel)); + } catch (EntityDoesNotExistException e) { + throw STATE_MODEL_NOT_FOUND; + } + } + + protected StateMessage getStateMessage() { + return getStateMessage(false); + } + + protected StateMessage getStateMessage(EntityInterface entity) { + return getStateMessage(entity, false); + } + + protected StateMessage getStateMessage(EntityInterface entity, boolean remove) { + Iterator<ToElementable> messages = entity.getMessages().iterator(); + while (messages.hasNext()) { + ToElementable s = messages.next(); + if (s instanceof StateMessage) { + if (remove) { + messages.remove(); + } + return (StateMessage) s; + } + } + return null; + } + + protected StateMessage getStateMessage(boolean remove) { + return getStateMessage(getEntity(), remove); + } + + protected Property getStateProperty(EntityInterface entity, boolean remove) { + Iterator<Property> it = entity.getProperties().iterator(); + while (it.hasNext()) { + Property p = it.next(); + if (Objects.equals(p.getName(), "State")) { + if (!(p.getDatatype() instanceof ReferenceDatatype)) { + continue; + } + if (remove) { + it.remove(); + } + return p; + } + } + return null; + } + + protected Property getStateProperty(boolean remove) { + return getStateProperty(getEntity(), remove); + } + + protected ClientMessage getStateClientMessage(EntityInterface entity, boolean remove) { + Iterator<Message> stateMessages = entity.getMessages("State").iterator(); + while (stateMessages.hasNext()) { + Message s = stateMessages.next(); + if (s instanceof ClientMessage) { + if (remove) { + stateMessages.remove(); + } + return (ClientMessage) s; + } + } + return null; + } + + protected ClientMessage getStateClientMessage(boolean remove) { + return getStateClientMessage(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 StateMessage createStateMessage(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()); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw COULD_NOT_GENERATE_STATE_MESSAGE; + } + } + + EntityInterface findStateModel(EntityInterface stateEntity) throws Exception { + Query query = + new Query( + "FIND Record StateModel WHICH REFERENCES " + Integer.toString(stateEntity.getId()), + getUser(), + null); + query.execute(getTransaction().getAccess()); + return retrieveValidSparseEntityById(query.getResultSet().get(0), null); + } +} diff --git a/src/main/java/org/caosdb/server/jobs/core/Inheritance.java b/src/main/java/org/caosdb/server/jobs/core/Inheritance.java index 086787e8bd4752ccf0a25f8684afa5a5c4a33c82..b3f91338883f16dcc1fd00b1fecbbae775f787b9 100644 --- a/src/main/java/org/caosdb/server/jobs/core/Inheritance.java +++ b/src/main/java/org/caosdb/server/jobs/core/Inheritance.java @@ -32,6 +32,7 @@ import org.caosdb.server.entity.Message.MessageType; import org.caosdb.server.entity.StatementStatus; import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.jobs.EntityJob; +import org.caosdb.server.jobs.ScheduledJob; import org.caosdb.server.transaction.Insert; import org.caosdb.server.transaction.Update; import org.caosdb.server.utils.EntityStatus; @@ -65,7 +66,7 @@ public class Inheritance extends EntityJob { protected void run() { if (getTransaction() instanceof Insert || getTransaction() instanceof Update) { if (getEntity().hasParents()) { - final ArrayList<EntityInterface> transfer = new ArrayList<EntityInterface>(); + final ArrayList<Property> transfer = new ArrayList<>(); parentLoop: for (final EntityInterface parent : getEntity().getParents()) { try { @@ -100,23 +101,25 @@ public class Inheritance extends EntityJob { // transfer properties if they are not implemented yet outerLoop: - for (final EntityInterface prop : transfer) { - for (final EntityInterface eprop : getEntity().getProperties()) { + for (final Property prop : transfer) { + for (final Property eprop : getEntity().getProperties()) { if (prop.hasId() && eprop.hasId() && prop.getId().equals(eprop.getId())) { continue outerLoop; } } // prop's Datatype might need to be resolved. - this.appendJob(prop, CheckDatatypePresent.class); - getEntity().addProperty(new Property(prop)); + ScheduledJob job = this.appendJob(prop, CheckDatatypePresent.class); + getTransaction().getSchedule().runJob(job); + + getEntity().addProperty(new Property(prop.getWrapped())); } } // implement properties if (getEntity().hasProperties()) { propertyLoop: - for (final EntityInterface property : getEntity().getProperties()) { - final ArrayList<EntityInterface> transfer = new ArrayList<EntityInterface>(); + for (final Property property : getEntity().getProperties()) { + final ArrayList<Property> transfer = new ArrayList<>(); try { if (property.getFlags().get("inheritance") == null) { break propertyLoop; @@ -156,15 +159,17 @@ public class Inheritance extends EntityJob { // transfer properties if they are not implemented yet outerLoop: - for (final EntityInterface prop : transfer) { - for (final EntityInterface eprop : property.getProperties()) { + for (final Property prop : transfer) { + for (final Property eprop : property.getProperties()) { if (prop.hasId() && eprop.hasId() && prop.getId() == eprop.getId()) { continue outerLoop; } } // prop's Datatype might need to be resolved. - this.appendJob(prop, CheckDatatypePresent.class); - property.addProperty(new Property(prop)); + ScheduledJob job = this.appendJob(prop, CheckDatatypePresent.class); + getTransaction().getSchedule().runJob(job); + + property.addProperty(new Property(prop.getWrapped())); } } } @@ -182,9 +187,9 @@ public class Inheritance extends EntityJob { * @param inheritance */ private void collectInheritedProperties( - List<EntityInterface> transfer, EntityInterface from, INHERITANCE_MODE inheritance) { + List<Property> transfer, EntityInterface from, INHERITANCE_MODE inheritance) { if (from.hasProperties()) { - for (final EntityInterface propProperty : from.getProperties()) { + for (final Property propProperty : from.getProperties()) { switch (inheritance) { // the following cases are ordered according to their importance level and use a // fall-through. diff --git a/src/main/java/org/caosdb/server/jobs/core/InitEntityState.java b/src/main/java/org/caosdb/server/jobs/core/InitEntityState.java new file mode 100644 index 0000000000000000000000000000000000000000..9a92dd42206ca798a7c6e60ef8ffe77d9dc9f488 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/InitEntityState.java @@ -0,0 +1,75 @@ +package org.caosdb.server.jobs.core; + +import java.util.Objects; +import org.caosdb.server.entity.ClientMessage; +import org.caosdb.server.entity.Entity; +import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.Message; +import org.caosdb.server.entity.Message.MessageType; +import org.caosdb.server.entity.UpdateEntity; +import org.caosdb.server.entity.wrapper.Property; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.JobExecutionTime; +import org.caosdb.server.transaction.WriteTransaction; +import org.caosdb.server.utils.EntityStatus; +import org.caosdb.server.utils.Observable; +import org.caosdb.server.utils.Observer; + +@JobAnnotation( + loadAlways = true, + time = JobExecutionTime.INIT, + transaction = WriteTransaction.class) +public class InitEntityState extends EntityStateJob implements Observer { + + @Override + protected void run() { + try { + State newState = initStateMessage(getEntity()); + try { + if (getEntity() instanceof UpdateEntity) { + State oldState = initStateMessage(((UpdateEntity) getEntity()).getOriginal()); + if (!Objects.equals(newState, oldState)) { + getEntity().acceptObserver(this); + } + } + } catch (Message m) { + getEntity().addWarning(STATE_ERROR_IN_ORIGINAL_ENTITY(m)); + } + } catch (Message m) { + getEntity().addError(m); + return; + } + } + + private static final Message STATE_ERROR_IN_ORIGINAL_ENTITY(Message m) { + return new Message( + 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); + entity.addMessage(stateMessage); + return stateMessage.getState(); + } + Property stateProperty = getStateProperty(entity, true); + if (stateProperty != null) { + StateMessage stateMessage = createStateMessage(stateProperty); + entity.addMessage(stateMessage); + return stateMessage.getState(); + } + return null; + } + + @Override + public boolean notifyObserver(String e, Observable o) { + if (e == Entity.ENTITY_STATUS_CHANGED_EVENT) { + if (o == getEntity() && getEntity().getEntityStatus() == EntityStatus.VALID) { + getEntity().setEntityStatus(EntityStatus.QUALIFIED); + return false; + } + } + return true; + } +} diff --git a/src/main/java/org/caosdb/server/jobs/core/ParseStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/ParseStateMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..d7cd71dc844b0b0001a0cab52b32d43a2560d843 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/ParseStateMessage.java @@ -0,0 +1,22 @@ +package org.caosdb.server.jobs.core; + +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.JobExecutionTime; +import org.caosdb.server.transaction.WriteTransaction; + +@JobAnnotation( + loadAlways = true, + transaction = WriteTransaction.class, + time = JobExecutionTime.PRE_TRANSACTION) +public class ParseStateMessage extends EntityStateJob { + + @Override + protected void run() { + StateMessage s = getStateMessage(true); + if (s != null) addStateProperty(s); + } + + private void addStateProperty(StateMessage stateEntity) { + getEntity().addProperty(stateEntity.createStateProperty()); + } +} diff --git a/src/main/java/org/caosdb/server/jobs/core/PickUp.java b/src/main/java/org/caosdb/server/jobs/core/PickUp.java index dff574a4ad0aa5199fe3ccdfdd9c72d2a7b2bb67..b91fccf72d54022c6f6d19ba26c2b0974a54829f 100644 --- a/src/main/java/org/caosdb/server/jobs/core/PickUp.java +++ b/src/main/java/org/caosdb/server/jobs/core/PickUp.java @@ -33,9 +33,10 @@ import org.caosdb.server.jobs.JobAnnotation; import org.caosdb.server.jobs.JobExecutionTime; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.Observable; +import org.caosdb.server.utils.Observer; @JobAnnotation(time = JobExecutionTime.INIT) -public class PickUp extends EntityJob { +public class PickUp extends EntityJob implements Observer { @Override protected void run() { diff --git a/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java b/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java index 950c61d36255b68f52e3066616b1dee5d9f259c3..e7e8f0e4d6aab3f3d0b3e9392d408a88aaa0abcc 100644 --- a/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java +++ b/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java @@ -34,14 +34,17 @@ import org.caosdb.server.entity.Message; import org.caosdb.server.entity.wrapper.Parent; import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.jobs.EntityJob; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.JobExecutionTime; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; /** - * To be called after CheckPropValid. + * To be called after CheckPropValid and Inheritance. * * @author tf */ +@JobAnnotation(time = JobExecutionTime.PRE_TRANSACTION) public class ProcessNameProperties extends EntityJob { @Override diff --git a/src/main/java/org/caosdb/server/jobs/core/WriteStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/WriteStateMessage.java new file mode 100644 index 0000000000000000000000000000000000000000..9fa21b7c0ee2248afa5606515fba858cd0e24361 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/WriteStateMessage.java @@ -0,0 +1,23 @@ +package org.caosdb.server.jobs.core; + +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; + +@JobAnnotation(loadAlways = true, time = JobExecutionTime.POST_TRANSACTION) +public class WriteStateMessage extends EntityStateJob { + + @Override + protected void run() { + try { + Property stateProperty = getStateProperty(true); + if (stateProperty != null) { + StateMessage stateMessage = createStateMessage(stateProperty); + getEntity().addMessage(stateMessage); + } + } catch (Message e) { + getEntity().addError(e); + } + } +} diff --git a/src/main/java/org/caosdb/server/transaction/Retrieve.java b/src/main/java/org/caosdb/server/transaction/Retrieve.java index 5b01657a9c0df55f2b93fe83532b074543debff9..087bc5a3d8b3268f308adda85b26253b25d8e4dc 100644 --- a/src/main/java/org/caosdb/server/transaction/Retrieve.java +++ b/src/main/java/org/caosdb/server/transaction/Retrieve.java @@ -30,6 +30,7 @@ import org.caosdb.server.entity.container.RetrieveContainer; import org.caosdb.server.entity.xml.SetFieldStrategy; import org.caosdb.server.entity.xml.ToElementStrategy; import org.caosdb.server.entity.xml.ToElementable; +import org.caosdb.server.jobs.ScheduledJob; import org.caosdb.server.jobs.core.Mode; import org.caosdb.server.jobs.core.RemoveDuplicates; import org.caosdb.server.jobs.core.ResolveNames; @@ -50,15 +51,19 @@ public class Retrieve extends Transaction<RetrieveContainer> { setAccess(getMonitor().acquiredWeakAccess(this)); // resolve names - final ResolveNames r = new ResolveNames(); - r.init(Mode.SHOULD, null, this); - getSchedule().add(r); - getSchedule().runJob(r); - - final RemoveDuplicates job = new RemoveDuplicates(); - job.init(Mode.MUST, null, this); - getSchedule().add(job); - getSchedule().runJob(job); + { + final ResolveNames r = new ResolveNames(); + r.init(Mode.SHOULD, null, this); + ScheduledJob scheduledJob = getSchedule().add(r); + getSchedule().runJob(scheduledJob); + } + + { + final RemoveDuplicates job = new RemoveDuplicates(); + job.init(Mode.MUST, null, this); + ScheduledJob scheduledJob = getSchedule().add(job); + getSchedule().runJob(scheduledJob); + } // make schedule for all parsed entities makeSchedule(); diff --git a/src/main/java/org/caosdb/server/transaction/Transaction.java b/src/main/java/org/caosdb/server/transaction/Transaction.java index 0235b8a25cd1d7df9f923023b1be2d5597038b0e..6fb28c6fa134c4945161422f77ace01079152fed 100644 --- a/src/main/java/org/caosdb/server/transaction/Transaction.java +++ b/src/main/java/org/caosdb/server/transaction/Transaction.java @@ -41,6 +41,7 @@ import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.jobs.Job; import org.caosdb.server.jobs.JobExecutionTime; import org.caosdb.server.jobs.Schedule; +import org.caosdb.server.jobs.ScheduledJob; import org.caosdb.server.jobs.core.AccessControl; import org.caosdb.server.jobs.core.CheckDatatypePresent; import org.caosdb.server.jobs.core.CheckEntityACLRoles; @@ -88,8 +89,8 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra protected void makeSchedule() throws Exception { // load flag jobs final Job loadContainerFlags = Job.getJob("LoadContainerFlagJobs", Mode.MUST, null, this); - this.schedule.add(loadContainerFlags); - this.schedule.runJob(loadContainerFlags); + ScheduledJob scheduledJob = this.schedule.add(loadContainerFlags); + this.schedule.runJob(scheduledJob); // AccessControl this.schedule.add(Job.getJob(AccessControl.class.getSimpleName(), Mode.MUST, null, this)); diff --git a/src/main/java/org/caosdb/server/transaction/Update.java b/src/main/java/org/caosdb/server/transaction/Update.java index f5a9a7ffd8b32e4b8d220ea6bba44731b51047ef..50d7f97868c2b5078292483ca6758c8be482305d 100644 --- a/src/main/java/org/caosdb/server/transaction/Update.java +++ b/src/main/java/org/caosdb/server/transaction/Update.java @@ -52,7 +52,20 @@ public class Update extends WriteTransaction<UpdateContainer> { } @Override - protected void preCheck() throws Exception {} + protected void preCheck() throws Exception { + for (final EntityInterface e : getContainer()) { + if (e.getEntityStatus() == EntityStatus.QUALIFIED) { + org.caosdb.server.entity.UpdateEntity newEntity = (org.caosdb.server.entity.UpdateEntity) e; + try { + checkPermissions(newEntity, deriveUpdate(newEntity, newEntity.getOriginal())); + } catch (final AuthorizationException exc) { + newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); + newEntity.addError(ServerMessages.AUTHORIZATION_ERROR); + newEntity.addInfo(exc.getMessage()); + } + } + } + } @Override protected void init() throws Exception { @@ -84,7 +97,8 @@ public class Update extends WriteTransaction<UpdateContainer> { execute(new RetrieveFullEntity(oldContainer), getAccess()); // Check if any updates are to be processed. - for (final EntityInterface newEntity : getContainer()) { + for (final EntityInterface e : getContainer()) { + org.caosdb.server.entity.UpdateEntity newEntity = (org.caosdb.server.entity.UpdateEntity) e; if (newEntity.getEntityStatus() == EntityStatus.QUALIFIED) { innerLoop: for (final EntityInterface oldEntity : oldContainer) { @@ -138,13 +152,7 @@ public class Update extends WriteTransaction<UpdateContainer> { } } - try { - checkPermissions(newEntity, deriveUpdate(newEntity, oldEntity)); - } catch (final AuthorizationException exc) { - newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); - newEntity.addError(ServerMessages.AUTHORIZATION_ERROR); - newEntity.addInfo(exc.getMessage()); - } + newEntity.setOriginal(oldEntity); } break innerLoop; }