catch up with branch daily

Signed-off-by: Ralf Mollik <ramollik@compex-commerce.com>
diff --git a/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedDescriptorWrapper.java b/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedDescriptorWrapper.java
new file mode 100644
index 0000000..744095f
--- /dev/null
+++ b/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedDescriptorWrapper.java
@@ -0,0 +1,167 @@
+package org.eclipse.osbp.jpa.services.history;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+import javax.persistence.Version;
+
+import org.eclipse.osbp.runtime.common.annotations.HistIsCurrent;
+import org.eclipse.osbp.runtime.common.annotations.HistIsCustomVersion;
+import org.eclipse.osbp.runtime.common.annotations.HistValidUntil;
+import org.eclipse.osbp.runtime.common.historized.UUIDHist;
+import org.eclipse.osbp.runtime.common.util.BeanUtils;
+import org.eclipse.persistence.descriptors.ClassDescriptor;
+import org.eclipse.persistence.internal.helper.DatabaseField;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+
+public class HistorizedDescriptorWrapper {
+
+	private final Class<?> clazz;
+	private final ClassDescriptor descriptor;
+	private DatabaseMapping versionMapping;
+	private DatabaseMapping validUntilMapping;
+	private DatabaseMapping currentMapping;
+	private DatabaseMapping isCustomVersionMapping;
+
+	private DatabaseMapping idMapping;
+	private ClassDescriptor uuidHistDescriptor;
+	private DatabaseMapping id_idMapping;
+	private DatabaseMapping id_validFromMapping;
+
+	public HistorizedDescriptorWrapper(ClassDescriptor descriptor) {
+		this.clazz = descriptor.getJavaClass();
+		this.descriptor = descriptor;
+
+		// find the ID String field by the descriptor
+		List<DatabaseField> pk = descriptor.getPrimaryKeyFields();
+		for (DatabaseMapping mapping : descriptor.getMappings()) {
+			if (mapping.isPrimaryKeyMapping()) {
+				idMapping = mapping;
+				break;
+			}
+		}
+
+		uuidHistDescriptor = idMapping.getReferenceDescriptor();
+
+		Field versionField = BeanUtils.getField(clazz, Version.class);
+		Field validUntilField = BeanUtils.getField(clazz, HistValidUntil.class);
+		Field currentHist = BeanUtils.getField(clazz, HistIsCurrent.class);
+		Field isCustomVersion = BeanUtils.getField(clazz, HistIsCustomVersion.class);
+
+		id_idMapping = uuidHistDescriptor.getMappingForAttributeName("id");
+		id_validFromMapping = uuidHistDescriptor.getMappingForAttributeName("validFrom");
+		versionMapping = descriptor.getMappingForAttributeName(versionField.getName());
+		validUntilMapping = descriptor.getMappingForAttributeName(validUntilField.getName());
+		currentMapping = descriptor.getMappingForAttributeName(currentHist.getName());
+		isCustomVersionMapping = descriptor.getMappingForAttributeName(isCustomVersion.getName());
+	}
+
+	public String getIdAttName() {
+		return idMapping.getAttributeName();
+	}
+
+	public String getId_IdAttName() {
+		return id_idMapping.getAttributeName();
+	}
+
+	public String getId_ValidFromAttName() {
+		return id_validFromMapping.getAttributeName();
+	}
+
+	public String getVersionAttName() {
+		return versionMapping.getAttributeName();
+	}
+
+	public String getValidUntilAttName() {
+		return validUntilMapping.getAttributeName();
+	}
+
+	public String getCurrentHistAttName() {
+		return currentMapping.getAttributeName();
+	}
+
+	public String getIsCustomVersionAttName() {
+		return isCustomVersionMapping.getAttributeName();
+	}
+
+	public UUIDHist getID(Object obj) {
+		return (UUIDHist) idMapping.getAttributeValueFromObject(obj);
+	}
+
+	public int getVersion(Object obj) {
+		return (int) versionMapping.getAttributeValueFromObject(obj);
+	}
+
+	public long getId_ValidFrom(Object obj) {
+		if (obj instanceof UUIDHist) {
+			return ((UUIDHist) obj).getValidFrom();
+		}
+		UUIDHist idHist = (UUIDHist) idMapping.getAttributeValueFromObject(obj);
+		return idHist != null ? idHist.getValidFrom() : -1;
+	}
+
+	public String getId_IdFrom(Object obj) {
+		if (obj instanceof UUIDHist) {
+			return ((UUIDHist) obj).getId();
+		}
+		UUIDHist idHist = (UUIDHist) idMapping.getAttributeValueFromObject(obj);
+		return idHist != null ? idHist.getId() : null;
+	}
+
+	public long getValidUntil(Object obj) {
+		return (long) validUntilMapping.getAttributeValueFromObject(obj);
+	}
+
+	public boolean isCurrentHist(Object obj) {
+		return (boolean) currentMapping.getAttributeValueFromObject(obj);
+	}
+
+	public boolean isCustomVersion(Object obj) {
+		return (boolean) isCustomVersionMapping.getAttributeValueFromObject(obj);
+	}
+
+	public Class<?> getEntityClass() {
+		return descriptor.getJavaClass();
+	}
+
+	public void setID(Object obj, Object value) {
+		idMapping.setAttributeValueInObject(obj, value);
+	}
+
+	public void setVersion(Object obj, int value) {
+		versionMapping.setAttributeValueInObject(obj, value);
+	}
+
+	public void setId_ValidFrom(Object obj, long value) {
+		if (obj instanceof UUIDHist) {
+			((UUIDHist) obj).setValidFrom(value);
+		}
+		UUIDHist idHist = (UUIDHist) idMapping.getAttributeValueFromObject(obj);
+		if (idHist != null) {
+			idHist.setValidFrom(value);
+		}
+	}
+
+	public void setId_IdFrom(Object obj, String value) {
+		if (obj instanceof UUIDHist) {
+			((UUIDHist) obj).setId(value);
+		}
+		UUIDHist idHist = (UUIDHist) idMapping.getAttributeValueFromObject(obj);
+		if (idHist != null) {
+			idHist.setId(value);
+		}
+	}
+
+	public void setValidUntil(Object obj, long value) {
+		validUntilMapping.setAttributeValueInObject(obj, value);
+	}
+
+	public void setCurrentHist(Object obj, boolean value) {
+		currentMapping.setAttributeValueInObject(obj, value);
+	}
+
+	public void setCustomVersion(Object obj, boolean value) {
+		isCustomVersionMapping.setAttributeValueInObject(obj, value);
+	}
+
+}
diff --git a/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedObjectWrapper.java b/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedObjectWrapper.java
new file mode 100644
index 0000000..a8173d9
--- /dev/null
+++ b/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedObjectWrapper.java
@@ -0,0 +1,109 @@
+package org.eclipse.osbp.jpa.services.history;
+
+import org.eclipse.osbp.runtime.common.historized.UUIDHist;
+import org.eclipse.persistence.descriptors.ClassDescriptor;
+
+/**
+ * This class gives access to historized annotated fields in the given obj.
+ */
+public class HistorizedObjectWrapper {
+
+	private HistorizedDescriptorWrapper delegate;
+	private Object obj;
+
+	public HistorizedObjectWrapper(ClassDescriptor descriptor, Object obj) {
+		delegate = new HistorizedDescriptorWrapper(descriptor);
+		this.obj = obj;
+	}
+
+	public String getIdAttName() {
+		return delegate.getIdAttName();
+	}
+
+	public String getVersionAttName() {
+		return delegate.getVersionAttName();
+	}
+
+	
+	
+	public String getId_IdAttName() {
+		return delegate.getId_IdAttName();
+	}
+
+	public String getId_ValidFromAttName() {
+		return delegate.getId_ValidFromAttName();
+	}
+
+	public long getId_ValidFrom() {
+		return delegate.getId_ValidFrom(obj);
+	}
+
+	public String getId_IdFrom() {
+		return delegate.getId_IdFrom(obj);
+	}
+
+	public void setId_ValidFrom(long value) {
+		delegate.setId_ValidFrom(obj, value);
+	}
+
+	public void setId_IdFrom(String value) {
+		delegate.setId_IdFrom(obj, value);
+	}
+
+	public String getValidUntilAttName() {
+		return delegate.getValidUntilAttName();
+	}
+
+	public String getCurrentHistAttName() {
+		return delegate.getCurrentHistAttName();
+	}
+
+	public String getIsCustomVersionAttName() {
+		return delegate.getIsCustomVersionAttName();
+	}
+
+	public UUIDHist getID() {
+		return delegate.getID(obj);
+	}
+
+	public long getVersion() {
+		return delegate.getVersion(obj);
+	}
+
+	public long getValidUntil() {
+		return delegate.getValidUntil(obj);
+	}
+
+	public boolean isCurrentHist() {
+		return delegate.isCurrentHist(obj);
+	}
+
+	public boolean isCustomVersion() {
+		return delegate.isCustomVersion(obj);
+	}
+
+	public Class<?> getEntityClass() {
+		return delegate.getEntityClass();
+	}
+
+	public void setID(Object value) {
+		delegate.setID(obj, value);
+	}
+
+	public void setVersion(int value) {
+		delegate.setVersion(obj, value);
+	}
+
+	public void setValidUntil(long value) {
+		delegate.setValidUntil(obj, value);
+	}
+
+	public void setCurrentHist(boolean value) {
+		delegate.setCurrentHist(obj, value);
+	}
+
+	public void setCustomVersion(boolean value) {
+		delegate.setCustomVersion(obj, value);
+	}
+
+}
diff --git a/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedQueryRedirector.java b/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedQueryRedirector.java
new file mode 100644
index 0000000..96168f5
--- /dev/null
+++ b/org.eclipse.osbp.jpa.services/src/org/eclipse/osbp/jpa/services/history/HistorizedQueryRedirector.java
@@ -0,0 +1,144 @@
+package org.eclipse.osbp.jpa.services.history;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.persistence.expressions.Expression;
+import org.eclipse.persistence.expressions.ExpressionBuilder;
+import org.eclipse.persistence.internal.sessions.AbstractRecord;
+import org.eclipse.persistence.internal.sessions.AbstractSession;
+import org.eclipse.persistence.internal.sessions.EmptyRecord;
+import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
+import org.eclipse.persistence.queries.DatabaseQuery;
+import org.eclipse.persistence.queries.InsertObjectQuery;
+import org.eclipse.persistence.queries.QueryRedirector;
+import org.eclipse.persistence.queries.ReadObjectQuery;
+import org.eclipse.persistence.queries.UpdateObjectQuery;
+import org.eclipse.persistence.queries.WriteObjectQuery;
+import org.eclipse.persistence.sessions.Record;
+import org.eclipse.persistence.sessions.Session;
+
+/**
+ * Manages the versioning of historized and timedependent entities.
+ */
+@SuppressWarnings("serial")
+public class HistorizedQueryRedirector implements QueryRedirector {
+
+	@Override
+	public Object invokeQuery(DatabaseQuery query, Record arguments, Session session) {
+		WriteObjectQuery writeObjectQuery = (WriteObjectQuery) query;
+
+		Object persistedObj = writeObjectQuery.getObject();
+
+		// the object that was persisted / merged
+		//
+		HistorizedObjectWrapper persistedObjWrapper = new HistorizedObjectWrapper(writeObjectQuery.getDescriptor(),
+				persistedObj);
+
+		if (persistedObjWrapper.getVersion() > 1) {
+			throw new IllegalArgumentException(
+					"Version must be 0. Changing historized records is not allowed: " + persistedObj.toString());
+		}
+		Object current = getCurrent(persistedObjWrapper, (AbstractSession) session);
+
+		if (current == null) {
+			persistedObjWrapper.setId_ValidFrom(createNow(persistedObjWrapper));
+			persistedObjWrapper.setValidUntil(getMaxDate());
+			persistedObjWrapper.setCurrentHist(true);
+
+			writeObjectQuery.setDoNotRedirect(true);
+			Object newAdr = writeObjectQuery.execute((AbstractSession) session, (AbstractRecord) arguments);
+			return newAdr;
+		} else {
+
+			// sleep 1ms for minimum time intervall between historized records
+			try {
+				Thread.sleep(1);
+			} catch (InterruptedException e) {
+			}
+
+			// update the address to be historized
+			long now = createNow(persistedObjWrapper);
+			Object histCurrentObj = getCurrentManaged(
+					new HistorizedObjectWrapper(writeObjectQuery.getDescriptor(), current), (AbstractSession) session);
+
+			// the current object in database with flag histCurrent = true
+			//
+			HistorizedObjectWrapper histCurrentObjWrapper = new HistorizedObjectWrapper(
+					writeObjectQuery.getDescriptor(), histCurrentObj);
+
+			if (query.getDescriptor().getObjectBuilder().compareObjects(persistedObj, histCurrentObj,
+					(AbstractSession) session)) {
+				return persistedObj;
+			}
+
+			histCurrentObjWrapper.setCurrentHist(false);
+			histCurrentObjWrapper.setValidUntil(now);
+			UpdateObjectQuery updateCurrent = new UpdateObjectQuery(histCurrentObj);
+			updateCurrent.setDoNotRedirect(true);
+			updateCurrent.setIsUserDefined(true);
+			updateCurrent.execute((AbstractSession) session, (AbstractRecord) arguments);
+
+			persistedObjWrapper.setId_ValidFrom(now);
+			persistedObjWrapper.setValidUntil(getMaxDate());
+			persistedObjWrapper.setCurrentHist(true);
+			persistedObjWrapper.setVersion((int) 0);
+
+			InsertObjectQuery insertObjectQ = new InsertObjectQuery(persistedObj);
+			insertObjectQ.setIsUserDefined(true);
+			insertObjectQ.setDoNotRedirect(true);
+
+			Object newAdr = insertObjectQ.execute((AbstractSession) session, (AbstractRecord) arguments);
+			return newAdr;
+		}
+
+	}
+
+	private long createNow(HistorizedObjectWrapper wrapper) {
+		if (wrapper.isCustomVersion()) {
+			return wrapper.getId_ValidFrom();
+		}
+		return new Date().getTime();
+	}
+
+	private Object getCurrent(HistorizedObjectWrapper wrapper, AbstractSession session) {
+
+		UnitOfWorkImpl uow = session.acquireNonSynchronizedUnitOfWork();
+
+		ReadObjectQuery rq = new ReadObjectQuery(wrapper.getEntityClass());
+
+		ExpressionBuilder eb = rq.getExpressionBuilder();
+		Expression exp = eb.get(wrapper.getIdAttName()).get(wrapper.getId_IdAttName()).equal(wrapper.getID().id)
+				.and(eb.get(wrapper.getCurrentHistAttName()).equal(true));
+		rq.setSelectionCriteria(exp);
+		rq.dontCheckCache();
+		rq.dontMaintainCache();
+
+		Object current = (Object) rq.executeInUnitOfWork(uow, EmptyRecord.getEmptyRecord());
+
+		uow.clearForClose(true);
+		return current;
+	}
+
+	private Object getCurrentManaged(HistorizedObjectWrapper wrapper, AbstractSession session) {
+
+		ReadObjectQuery rq = new ReadObjectQuery(wrapper.getEntityClass());
+
+		ExpressionBuilder eb = rq.getExpressionBuilder();
+		Expression exp = eb.get(wrapper.getIdAttName()).equal(wrapper.getID());
+		rq.setSelectionCriteria(exp);
+
+		Object current = rq.execute(session, EmptyRecord.getEmptyRecord());
+
+		return current;
+	}
+
+	private long getMaxDate() {
+		Calendar cal = Calendar.getInstance();
+		cal.set(2099, Calendar.DECEMBER, 31, 0, 0, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		Date maxDate = cal.getTime();
+		return maxDate.getTime();
+	}
+
+}