package org.jboss.ejb.plugins.cmp.jdbc.bridge;
import java.lang.reflect.Field;
import javax.ejb.EJBException;
import org.jboss.deployment.DeploymentException;
import org.jboss.ejb.EntityEnterpriseContext;
import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext;
import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager;
import org.jboss.ejb.plugins.cmp.jdbc.JDBCType;
import org.jboss.ejb.plugins.cmp.jdbc.CMPFieldStateFactory;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
public class JDBCCMP2xFieldBridge extends JDBCAbstractCMPFieldBridge
{
private final String columnName;
private final JDBCCMP2xFieldBridge cmpFieldIAmMappedTo;
private ChainLink cmrChainLink;
public JDBCCMP2xFieldBridge(JDBCStoreManager manager,
JDBCCMPFieldMetaData metadata)
throws DeploymentException
{
super(manager, metadata);
cmpFieldIAmMappedTo = null;
columnName = metadata.getColumnName();
}
public JDBCCMP2xFieldBridge(JDBCStoreManager manager,
JDBCCMPFieldMetaData metadata,
CMPFieldStateFactory stateFactory,
boolean checkDirtyAfterGet)
throws DeploymentException
{
this(manager, metadata);
this.stateFactory = stateFactory;
this.checkDirtyAfterGet = checkDirtyAfterGet;
}
public JDBCCMP2xFieldBridge(JDBCCMP2xFieldBridge cmpField,
CMPFieldStateFactory stateFactory,
boolean checkDirtyAfterGet)
throws DeploymentException
{
this(
(JDBCStoreManager) cmpField.getManager(),
cmpField.getFieldName(),
cmpField.getFieldType(),
cmpField.getJDBCType(),
cmpField.isReadOnly(), cmpField.getReadTimeOut(),
cmpField.getPrimaryKeyClass(),
cmpField.getPrimaryKeyField(),
cmpField,
null, cmpField.getColumnName()
);
this.stateFactory = stateFactory;
this.checkDirtyAfterGet = checkDirtyAfterGet;
}
public JDBCCMP2xFieldBridge(JDBCStoreManager manager,
JDBCCMPFieldMetaData metadata,
JDBCType jdbcType)
throws DeploymentException
{
super(manager, metadata, jdbcType);
cmpFieldIAmMappedTo = null;
columnName = metadata.getColumnName();
}
public JDBCCMP2xFieldBridge(JDBCStoreManager manager,
String fieldName,
Class fieldType,
JDBCType jdbcType,
boolean readOnly,
long readTimeOut,
Class primaryKeyClass,
Field primaryKeyField,
JDBCCMP2xFieldBridge cmpFieldIAmMappedTo,
JDBCCMRFieldBridge myCMRField,
String columnName)
throws DeploymentException
{
super(
manager,
fieldName,
fieldType,
jdbcType,
readOnly,
readTimeOut,
primaryKeyClass,
primaryKeyField,
cmpFieldIAmMappedTo.getFieldIndex(),
cmpFieldIAmMappedTo.getTableIndex(),
cmpFieldIAmMappedTo.checkDirtyAfterGet,
cmpFieldIAmMappedTo.stateFactory
);
this.cmpFieldIAmMappedTo = cmpFieldIAmMappedTo;
if(myCMRField != null)
{
cmrChainLink = new CMRChainLink(myCMRField);
cmpFieldIAmMappedTo.addCMRChainLink(cmrChainLink);
}
this.columnName = columnName;
}
public JDBCCMP2xFieldBridge getCmpFieldIAmMappedTo()
{
return cmpFieldIAmMappedTo;
}
public ChainLink getCmrChainLink()
{
return cmrChainLink;
}
public boolean isFKFieldMappedToCMPField()
{
return cmpFieldIAmMappedTo != null && this.cmrChainLink != null;
}
public String getColumnName()
{
return columnName;
}
public Object getInstanceValue(EntityEnterpriseContext ctx)
{
FieldState fieldState = getLoadedState(ctx);
return fieldState.getValue();
}
public void setInstanceValue(EntityEnterpriseContext ctx, Object value)
{
FieldState fieldState = getFieldState(ctx);
if(cmpFieldIAmMappedTo != null && cmpFieldIAmMappedTo.isPrimaryKeyMember())
{
if(value != null)
{
if(fieldState.isLoaded() && fieldState.isValueChanged(value))
{
throw new IllegalStateException(
"New value [" + value + "] of a foreign key field "
+ getFieldName()
+ " changed the value of a primary key field "
+ cmpFieldIAmMappedTo.getFieldName()
+ "[" + fieldState.value + "]"
);
}
else
{
fieldState.setValue(value);
}
}
}
else
{
if(cmrChainLink != null
&& JDBCEntityBridge.isEjbCreateDone(ctx)
&& fieldState.isLoaded()
&& fieldState.isValueChanged(value))
{
cmrChainLink.execute(ctx, fieldState, value);
}
fieldState.setValue(value);
}
fieldState.setLoaded();
}
public void lockInstanceValue(EntityEnterpriseContext ctx)
{
getFieldState(ctx).lockValue();
}
public boolean isLoaded(EntityEnterpriseContext ctx)
{
return getFieldState(ctx).isLoaded();
}
public boolean isDirty(EntityEnterpriseContext ctx)
{
return !primaryKeyMember
&& !readOnly
&& getFieldState(ctx).isDirty();
}
public void setClean(EntityEnterpriseContext ctx)
{
FieldState fieldState = getFieldState(ctx);
fieldState.setClean();
if(readOnly && readTimeOut != -1)
fieldState.lastRead = System.currentTimeMillis();
}
public void resetPersistenceContext(EntityEnterpriseContext ctx)
{
if(isReadTimedOut(ctx))
{
JDBCContext jdbcCtx = (JDBCContext)ctx.getPersistenceContext();
FieldState fieldState = (FieldState)jdbcCtx.getFieldState(jdbcContextIndex);
if(fieldState != null)
fieldState.reset();
}
}
public boolean isReadTimedOut(EntityEnterpriseContext ctx)
{
if(!readOnly)
return true;
if(readTimeOut == -1)
return false;
long readInterval = System.currentTimeMillis() - getFieldState(ctx).lastRead;
return readInterval >= readTimeOut;
}
public Object getLockedValue(EntityEnterpriseContext ctx)
{
return getLoadedState(ctx).getLockedValue();
}
public void updateState(EntityEnterpriseContext ctx, Object value)
{
getFieldState(ctx).updateState(value);
}
protected void setDirtyAfterGet(EntityEnterpriseContext ctx)
{
getFieldState(ctx).setCheckDirty();
}
private FieldState getLoadedState(EntityEnterpriseContext ctx)
{
FieldState fieldState = getFieldState(ctx);
if(!fieldState.isLoaded())
{
manager.loadField(this, ctx);
if(!fieldState.isLoaded())
throw new EJBException("Could not load field value: " + getFieldName());
}
return fieldState;
}
private void addCMRChainLink(ChainLink nextCMRChainLink)
{
if(cmrChainLink == null)
{
cmrChainLink = new DummyChainLink();
}
cmrChainLink.setNextLink(nextCMRChainLink);
}
private FieldState getFieldState(EntityEnterpriseContext ctx)
{
JDBCContext jdbcCtx = (JDBCContext)ctx.getPersistenceContext();
FieldState fieldState = (FieldState)jdbcCtx.getFieldState(jdbcContextIndex);
if(fieldState == null)
{
fieldState = new FieldState(jdbcCtx);
jdbcCtx.setFieldState(jdbcContextIndex, fieldState);
}
return fieldState;
}
private class FieldState
{
private JDBCEntityBridge.EntityState entityState;
private Object value;
private Object state;
private Object lockedValue;
private long lastRead = -1;
public FieldState(JDBCContext jdbcCtx)
{
this.entityState = jdbcCtx.getEntityState();
}
public Object getValue()
{
return value;
}
public void setValue(Object newValue)
{
this.value = newValue;
setCheckDirty();
}
private void setCheckDirty()
{
entityState.setCheckDirty(tableIndex);
}
public boolean isLoaded()
{
return entityState.isLoaded(tableIndex);
}
public void setLoaded()
{
entityState.setLoaded(tableIndex);
}
public boolean isDirty()
{
return isLoaded() && !stateFactory.isStateValid(state, value);
}
public boolean isValueChanged(Object newValue)
{
return value == null ? newValue != null : !value.equals(newValue);
}
public void setClean()
{
entityState.setClean(tableIndex);
updateState(value);
}
private void updateState(Object value)
{
state = stateFactory.getFieldState(value);
lockedValue = value;
}
public void reset()
{
value = null;
state = null;
lastRead = -1;
entityState.resetFlags(tableIndex);
}
public void lockValue()
{
if(entityState.lockValue(tableIndex))
{
lockedValue = value;
}
}
public Object getLockedValue()
{
return lockedValue;
}
}
private abstract static class ChainLink
{
private ChainLink nextLink;
public ChainLink()
{
nextLink = this;
}
public void setNextLink(ChainLink nextLink)
{
nextLink.nextLink = this.nextLink;
this.nextLink = nextLink;
}
public ChainLink getNextLink()
{
return nextLink;
}
public void execute(EntityEnterpriseContext ctx,
FieldState fieldState,
Object newValue)
{
nextLink.doExecute(this, ctx, fieldState, newValue);
}
protected abstract void doExecute(ChainLink originator,
EntityEnterpriseContext ctx,
FieldState fieldState,
Object newValue);
}
private static class CMRChainLink
extends ChainLink
{
private final JDBCCMRFieldBridge cmrField;
public CMRChainLink(JDBCCMRFieldBridge cmrField)
{
this.cmrField = cmrField;
}
public void doExecute(ChainLink originator,
EntityEnterpriseContext ctx,
FieldState fieldState,
Object newValue)
{
Object oldRelatedId = cmrField.getRelatedIdFromContext(ctx);
if(originator != getNextLink())
{
getNextLink().doExecute(originator, ctx, fieldState, newValue);
}
fieldState.setValue(newValue);
Object newRelatedId = cmrField.getRelatedIdFromContext(ctx);
if(oldRelatedId != null)
destroyRelations(oldRelatedId, ctx);
if(newRelatedId != null)
createRelations(newRelatedId, ctx);
}
private void createRelations(Object newRelatedId, EntityEnterpriseContext ctx)
{
try
{
if(cmrField.isForeignKeyValid(newRelatedId))
{
cmrField.createRelationLinks(ctx, newRelatedId, false);
}
else
{
cmrField.setForeignKey(ctx, newRelatedId);
if(ctx.getId() != null)
{
JDBCCMRFieldBridge relatedCMRField = (JDBCCMRFieldBridge)cmrField.getRelatedCMRField();
relatedCMRField.addRelatedPKWaitingForMyPK(newRelatedId, ctx.getId());
}
}
}
catch(Exception e)
{
}
}
private void destroyRelations(Object oldRelatedId, EntityEnterpriseContext ctx)
{
JDBCCMRFieldBridge relatedCMRField = (JDBCCMRFieldBridge)cmrField.getRelatedCMRField();
relatedCMRField.removeRelatedPKWaitingForMyPK(oldRelatedId, ctx.getId());
try
{
if(cmrField.isForeignKeyValid(oldRelatedId))
{
cmrField.destroyRelationLinks(ctx, oldRelatedId, true, false);
}
}
catch(Exception e)
{
}
}
}
private static class DummyChainLink
extends ChainLink
{
public void doExecute(ChainLink originator,
EntityEnterpriseContext ctx,
FieldState fieldState,
Object newValue)
{
if(originator != getNextLink())
{
getNextLink().doExecute(originator, ctx, fieldState, newValue);
}
fieldState.setValue(newValue);
}
}
}