package pt.ist.fenixframework.core;
import java.io.ObjectStreamException;
import java.io.Serializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pt.ist.fenixframework.DomainObject;
/**
* This is the top-level class for every DomainObject. Each backend should provide a subclass of
* this class, with a backend-specific implementation of both {@link DomainObject#getExternalId()}
* and {@link #getOid()}. In this class, they simply throw an {@link
* UnsupportedOperationException}. The method {@link DomainObject#getExternalId()} should be used
* by the user of the framework, whereas the method {@link #getOid()} should be used by code within
* the framework. This allows for a more efficient implementation of the object's internal
* identifier, other than the String type imposed on the external identifier.
*
* Additionally, the subclass must also implement {@link #ensureOid()}, {@link
* #makeSerializedForm()}, and {@link SerializedForm#fromExternalId(String)}. See their
* documentation for further explanation.
*/
public abstract class AbstractDomainObject implements DomainObject {
private static final Logger logger = LoggerFactory.getLogger(AbstractDomainObject.class);
@Override
public String getExternalId() {
throw new UnsupportedOperationException("getExternalId not implemented at this level");
}
/**
* Returns an internal global representation of this object. Although, it can be implemented
* simply as a call to {@link #getExternalId()}, this method enables code **within** the
* framework to leverage on the knowledge of the concrete identifier, thus being more efficient.
* <strong>This method should only be used by code internal to the framework</strong>.
*/
public Comparable getOid() {
throw new UnsupportedOperationException("getOid not implemented at this level");
}
/**
* Default, no-arg constructor. Calls {@link #ensureOid()} to set the object's identifier.
* Every {@link DomainObject} constructor (except for the special allocate-instance constructor)
* should ensure that {@link #ensureOid()} is called once during object creation.
*
* @see #ensureOid
*/
protected AbstractDomainObject() {
super();
ensureOid();
}
/**
* This constructor exists only as part of the allocate-instance protocol and should never be
* explicitly invoked by the programmer. Each backend must implement this constructor, and
* decide how the OID gets restored to the object. Note that the classes modelled in DML are
* automatically injected with this constructor, which simply delegates the operation to their
* superclass's constructor.
*
* In this class, this constructor is empty. It is here just as a placeholder for this
* documentation.
*/
protected AbstractDomainObject(DomainObjectAllocator.OID oid) { }
/**
* Set the identifier (<code>oid</code>) of the object that is being created. This method is
* invoked by the no-arg constructor. It is only intented to be invoked from within a
* constructor (with the exception of {@link #AbstractDomainObject(DomainObjectAllocator.OID)},
* which has its own way of restoring the object's id.
*/
protected void ensureOid() {
throw new UnsupportedOperationException("ensureOid not implemented at this level");
}
@Override
public final int hashCode() {
return super.hashCode();
}
@Override
public final boolean equals(Object obj) {
return super.equals(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
/* This method must be in the format:
*
* protected Object writeReplace() throws ObjectStreamException;
*
* to allow subclasses to have this writeReplace action when serialization occurs.
*/
protected Object writeReplace() throws ObjectStreamException {
if (logger.isTraceEnabled()) {
logger.trace("Serializing " + this.getClass().getName() + ":" + this.getExternalId());
}
return makeSerializedForm();
}
/**
* Creates the concrete instance of SerializedForm for this DomainObject. This method is
* invoked when serialization of the DomainObject occurs. Final users of this framework should
* never invoke this method explicitly. Backend developers should provide an implementation for
* this method in their subclasses of AbstractDomainObject.
*
* @return The corresponding SerializedForm
*/
protected SerializedForm makeSerializedForm() {
throw new UnsupportedOperationException("makeSerializedForm not implemented at this level");
}
protected static abstract class SerializedForm implements Serializable {
private static final long serialVersionUID = 1L;
// the external serialized form of any domain object only needs to keep its external ID
private final String externalId;
protected SerializedForm(AbstractDomainObject obj) {
externalId = obj.getExternalId();
}
/* This method must be in the format:
*
* protected Object readResolve() throws ObjectStreamException;
*
* to allow subclasses to have this readResolve action when de-serialization occurs.
*/
protected Object readResolve() throws ObjectStreamException {
if (logger.isTraceEnabled()) {
logger.trace("Deserializing " + this.externalId);
}
return fromExternalId(externalId);
}
/**
* Returns a DomainObject given its externalId. This method is invoked when
* de-serialization of a previously serialized DomainObject occurs.
*
* Final users of this framework should not invoke this method explicitly. Backend
* developers should provide an implementation for this method in their subclasses of
* AbstractDomainObject.SerializedForm.
*
* @param externalId The object's external identifier
* @return The corresponding DomainObject
*/
protected DomainObject fromExternalId(String externalId) {
throw new UnsupportedOperationException("fromExternalID not implemented at this level");
}
}
// 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();
}
}