/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ 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.metadata.JDBCCMPFieldMetaData; /** * JDBCCMP1xFieldBridge is a concrete implementation of JDBCCMPFieldBridge for * CMP version 1.x. Getting and setting of instance fields set the * corresponding field in bean instance. Dirty checking is performed by * storing the current value in the entity persistence context when ever * setClean is called, and comparing current value to the original value. * * Life-cycle: * Tied to the EntityBridge. * * Multiplicity: * One for each entity bean cmp field. * * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a> * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a> * @version $Revision: 81030 $ */ public class JDBCCMP1xFieldBridge extends JDBCAbstractCMPFieldBridge { private Field field; public JDBCCMP1xFieldBridge(JDBCStoreManager manager, JDBCCMPFieldMetaData metadata) throws DeploymentException { super(manager, metadata); try { field = manager.getMetaData().getEntityClass().getField(getFieldName()); } catch(NoSuchFieldException e) { // Non recoverable internal exception throw new DeploymentException("No field named '" + getFieldName() + "' found in entity class."); } } public Object getInstanceValue(EntityEnterpriseContext ctx) { FieldState fieldState = getFieldState(ctx); if(!fieldState.isLoaded()) { throw new EJBException("CMP 1.1 field not loaded: " + getFieldName()); } try { return field.get(ctx.getInstance()); } catch(Exception e) { // Non recoverable internal exception throw new EJBException("Internal error getting instance field " + getFieldName(), e); } } public void setInstanceValue(EntityEnterpriseContext ctx, Object value) { try { field.set(ctx.getInstance(), value); FieldState fieldState = getFieldState(ctx); fieldState.setLoaded(); fieldState.setCheckDirty(); } catch(Exception e) { // Non recoverable internal exception throw new EJBException("Internal error setting instance field " + getFieldName(), e); } } public Object getLockedValue(EntityEnterpriseContext ctx) { throw new UnsupportedOperationException("Optimistic locking is not supported in CMP1.1."); } public void lockInstanceValue(EntityEnterpriseContext ctx) { // not supported } public boolean isLoaded(EntityEnterpriseContext ctx) { return getFieldState(ctx).isLoaded(); } /** * Has the value of this field changes since the last time clean was called. */ public boolean isDirty(EntityEnterpriseContext ctx) { // read only and primary key fields are never dirty if(isReadOnly() || isPrimaryKeyMember()) { return false; } // has the value changes since setClean return isLoaded(ctx) && !stateFactory.isStateValid(getInstanceValue(ctx), getFieldState(ctx).originalValue); } /** * Mark this field as clean. * Saves the current state in context, so it can be compared when * isDirty is called. */ public void setClean(EntityEnterpriseContext ctx) { FieldState fieldState = getFieldState(ctx); fieldState.originalValue = getInstanceValue(ctx); // update last read time if(isReadOnly()) { fieldState.lastRead = System.currentTimeMillis(); } } public boolean isReadTimedOut(EntityEnterpriseContext ctx) { // if we are read/write then we are always timed out if(!isReadOnly()) { return true; } // if read-time-out is -1 then we never time out. if(getReadTimeOut() == -1) { return false; } long readInterval = System.currentTimeMillis() - getFieldState(ctx).lastRead; return readInterval >= getReadTimeOut(); } public void resetPersistenceContext(EntityEnterpriseContext ctx) { if(isReadTimedOut(ctx)) { JDBCContext jdbcCtx = (JDBCContext)ctx.getPersistenceContext(); FieldState fieldState = (FieldState)jdbcCtx.getFieldState(jdbcContextIndex); if(fieldState != null) fieldState.reset(); } } protected void setDirtyAfterGet(EntityEnterpriseContext ctx) { getFieldState(ctx).setCheckDirty(); } 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 Object originalValue; private long lastRead = -1; private JDBCEntityBridge.EntityState entityState; public FieldState(JDBCContext jdbcContext) { this.entityState = jdbcContext.getEntityState(); } public boolean isLoaded() { return entityState.isLoaded(tableIndex); } public void setLoaded() { entityState.setLoaded(tableIndex); } public void setCheckDirty() { entityState.setCheckDirty(tableIndex); } public void reset() { originalValue = null; lastRead = -1; entityState.resetFlags(tableIndex); log.debug("reset field state"); } } }