package pt.ist.fenixframework.pstm; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.reflect.Field; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.ojb.broker.PersistenceBroker; import org.apache.ojb.broker.metadata.ClassDescriptor; import pt.ist.fenixframework.DomainObject; import pt.ist.fenixframework.FenixFramework; public abstract class AbstractDomainObject implements DomainObject, dml.runtime.FenixDomainObject, Serializable { // this should be final, but the ensureIdInternal method prevents it private long oid; public class UnableToDetermineIdException extends RuntimeException { public UnableToDetermineIdException(Throwable cause) { super("Unable to determine id Exception", cause); } } protected AbstractDomainObject() { super(); // All domain objects become persistent upon their creation. // Ensure that this object gets an idInternal // jcachopo: This should be changed in the future... ensureIdInternal(); // ensureOid(); Transaction.storeNewObject(this); } protected AbstractDomainObject(DomainObjectAllocator.OID oid) { // this constructor exists only as part of the allocate-instance // protocol this.oid = oid.oid; } public final Integer getIdInternal() { return (int) (this.oid & 0x7FFFFFFF); } private Integer get$idInternal() { return getIdInternal(); } protected void ensureOid() { try { // find successive ids until one is available while (true) { this.oid = DomainClassInfo.getNextOidFor(this.getClass()); Object cached = Transaction.getCache().cache(this); if (cached == this) { // break the loop once we got this instance cached return; } } } catch (Exception e) { throw new UnableToDetermineIdException(e); } } protected void ensureIdInternal() { try { PersistenceBroker broker = Transaction.getOJBBroker(); Class myClass = this.getClass(); ClassDescriptor cld = broker.getClassDescriptor(myClass); long cid = ((long) DomainClassInfo.mapClassToId(myClass) << 32); // find successive ids until one is available while (true) { Integer id = (Integer) broker.serviceSequenceManager().getUniqueValue(cld.getFieldDescriptorByName("idInternal")); this.oid = cid + id; Object cached = Transaction.getCache().cache(this); if (cached == this) { // break the loop once we got this instance cached return; } } } catch (Exception e) { throw new UnableToDetermineIdException(e); } } @Override public final int hashCode() { return super.hashCode(); } @Override public final boolean equals(Object obj) { return super.equals(obj); } public long getOID() { return getOid(); } // duplicate method (see getOID()). This is the name that should stick. // the other is to go away public long getOid() { return oid; } private long get$oid() { return getOid(); } public static <T extends DomainObject> T fromOID(long oid) { DomainObject obj = Transaction.getCache().lookup(oid); if (obj == null) { obj = DomainObjectAllocator.allocateObject(oid); // cache object and return the canonical object obj = Transaction.getCache().cache(obj); } return (T) obj; } VersionedSubject getSlotNamed(String attrName) { Class myClass = this.getClass(); while (myClass != Object.class) { try { Field f = myClass.getDeclaredField(attrName); f.setAccessible(true); return (VersionedSubject) f.get(this); } catch (NoSuchFieldException nsfe) { myClass = myClass.getSuperclass(); } catch (IllegalAccessException iae) { throw new Error("Couldn't find attribute " + attrName + ": " + iae); } catch (SecurityException se) { throw new Error("Couldn't find attribute " + attrName + ": " + se); } } return null; } Object getCurrentValueFor(String attrName) { return getSlotNamed(attrName).getCurrentValue(this, attrName); } jvstm.VBoxBody addNewVersion(String attrName, int txNumber) { VersionedSubject vs = getSlotNamed(attrName); if (vs != null) { return vs.addNewVersion(attrName, txNumber); } System.out.println("!!! WARNING !!!: addNewVersion couldn't find the appropriate slot"); return null; } public void readFromResultSet(java.sql.ResultSet rs) throws java.sql.SQLException { int txNumber = Transaction.current().getNumber(); readSlotsFromResultSet(rs, txNumber); } protected abstract void readSlotsFromResultSet(java.sql.ResultSet rs, int txNumber) throws java.sql.SQLException; public boolean isDeleted() { throw new UnsupportedOperationException(); } protected void checkDisconnected() { } protected void handleAttemptToDeleteConnectedObject() { if (FenixFramework.getConfig().isErrorfIfDeletingObjectNotDisconnected()) { throw new Error("Trying to delete a DomainObject that is still connected to other objects: " + this); } else { System.out.println("!!! WARNING !!!: Deleting a DomainObject that is still connected to other objects: " + this); } } protected void deleteDomainObject() { checkDisconnected(); Transaction.deleteObject(this); } protected int getColumnIndex(final ResultSet resultSet, final String columnName, final Integer[] columnIndexes, final int columnCount) throws SQLException { if (columnIndexes[columnCount] == null) { synchronized (columnIndexes) { if (columnIndexes[columnCount] == null) { int columnIndex = Integer.valueOf(resultSet.findColumn(columnName)); columnIndexes[columnCount] = columnIndex; } } } return columnIndexes[columnCount].intValue(); } // serialization code protected Object writeReplace() throws ObjectStreamException { return new SerializedForm(this); } private static class SerializedForm implements Serializable { private static final long serialVersionUID = 1L; // use string to allow future expansion of an OID private final String oid; SerializedForm(AbstractDomainObject obj) { this.oid = String.valueOf(obj.getOid()); } Object readResolve() throws ObjectStreamException { long objOid = Long.parseLong(this.oid); return AbstractDomainObject.fromOID(objOid); } } public final String getExternalId() { return String.valueOf(getOID()); } public static <T extends DomainObject> T fromExternalId(String extId) { if (extId == null) { return null; } else { return AbstractDomainObject.<T> fromOID(Long.valueOf(extId)); } } protected void doCheckDisconnectedAction(java.util.List<String> relationList) { for(String relation : relationList) { System.out.println("Relation not disconnected" + relation); } } @Override public String toString() { return getClass().getName() + ":" + getExternalId(); } }