/*
* 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.aspects.versioned;
import org.jboss.aop.Advised;
import org.jboss.aop.ClassAdvisor;
import org.jboss.aop.InstanceAdvised;
import org.jboss.logging.Logger;
import org.jboss.tm.TransactionLocal;
import org.jboss.util.id.GUID;
import javax.naming.InitialContext;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @version $Revision: 80997 $
*/
public class DistributedPOJOState extends StateManager implements DistributedState, java.io.Externalizable
{
private static final long serialVersionUID = 7640633352012924284L;
/**
* Logging instance
*/
private static Logger log = Logger.getLogger(DistributedPOJOState.class);
protected String classname;
protected HashMap fieldMap;
transient protected TransactionManager tm;
transient protected WeakReference advisedRef;
transient protected TransactionLocal txState = new TransactionLocal();
transient protected SynchronizationManager synchManager;
transient protected DistributedVersionManager versionManager;
public DistributedPOJOState() {}
public DistributedPOJOState(GUID daguid, long datimeout, Advised advised, DistributedVersionManager versionManager, SynchronizationManager synchManager)
throws Exception
{
super(daguid, datimeout);
this.fieldMap = new HashMap();
this.classname = advised.getClass().getName();
InitialContext ctx = new InitialContext();
this.tm = (TransactionManager)ctx.lookup("java:/TransactionManager");
this.synchManager = synchManager;
this.versionManager = versionManager;
this.advisedRef = new WeakReference(advised);
}
public InstanceAdvised getObject()
{
if (advisedRef != null)
{
return (InstanceAdvised)advisedRef.get();
}
return null;
}
public boolean equals(Object obj)
{
if (!(obj instanceof DistributedPOJOState)) return false;
DistributedPOJOState pojo = (DistributedPOJOState)obj;
return guid.equals(pojo.guid);
}
public int hashCode()
{
return guid.hashCode();
}
public InstanceAdvised buildObject(SynchronizationManager manager, DistributedVersionManager versionManager)
throws Exception
{
log.trace("building a " + classname + " of guid " + guid);
this.versionManager = versionManager;
this.synchManager = manager;
Class clazz = Thread.currentThread().getContextClassLoader().loadClass(classname);
Advised advised = (Advised)clazz.newInstance();
this.advisedRef = new WeakReference(advised);
versionManager.addVersioning(this, advised);
manager.putState(guid, this);
manager.putObject(guid, advised);
Iterator it = fieldMap.values().iterator();
while(it.hasNext())
{
DistributedFieldUpdate update = (DistributedFieldUpdate)it.next();
ClassAdvisor advisor = (ClassAdvisor)advised._getAdvisor();
log.trace("build field " + advisor.getAdvisedFields()[update.getFieldIndex()].getName());
Object val = update.getNonDereferencedValue();
if (val != null && (val instanceof VersionReference))
{
VersionReference ref = (VersionReference)val;
log.trace("VersionReference.guid: " + ref.getGUID() + " for field " + advisor.getAdvisedFields()[update.getFieldIndex()].getName());
val = manager.getObject(ref.getGUID());
if (val == null)
{
DistributedState fieldVal = manager.getState(ref.getGUID());
val = fieldVal.buildObject(manager, versionManager);
}
ref.set((InstanceAdvised)val);
}
}
return advised;
}
public HashMap getTxState()
{
return (HashMap)txState.get();
}
public HashMap getTxState(Transaction tx)
{
return (HashMap)txState.get(tx);
}
public Object fieldRead(org.jboss.aop.joinpoint.Invocation invocation) throws Throwable
{
acquireReadLock();
try
{
org.jboss.aop.joinpoint.FieldReadInvocation fieldInvocation = (org.jboss.aop.joinpoint.FieldReadInvocation)invocation;
Integer index = new Integer(fieldInvocation.getIndex());
HashMap map = getTxState();
if (map == null)
{
map = fieldMap;
}
DistributedFieldUpdate update = (DistributedFieldUpdate)map.get(index);
Object val = update.getValue();
return val;
}
finally
{
releaseReadLock();
}
}
public Object fieldWrite(org.jboss.aop.joinpoint.Invocation invocation) throws Throwable
{
org.jboss.aop.joinpoint.FieldWriteInvocation fieldInvocation = (org.jboss.aop.joinpoint.FieldWriteInvocation)invocation;
Integer index = new Integer(fieldInvocation.getIndex());
Object val = fieldInvocation.getValue();
if (val instanceof Advised)
{
Advised advisedValue = (Advised)val;
val = versionManager.makeVersioned(advisedValue);
}
Transaction tx = tm.getTransaction();
if (tx == null)
{
acquireWriteLock();
try
{
// REVISIT: Handle exception
DistributedFieldUpdate update = (DistributedFieldUpdate)fieldMap.get(index);
long versionId = update.getVersionId() + 1;
update.setVersionId(versionId);
update.setValue(val);
HashMap fieldUpdates = new HashMap();
fieldUpdates.put(index, update);
synchManager.noTxUpdate(new DistributedPOJOUpdate(guid, fieldUpdates));
return null;
}
finally
{
releaseWriteLock();
}
}
acquireReadLock();
try
{
HashMap map = (HashMap)txState.get();
if (map == null)
{
map = new HashMap();
DistributedFieldUpdate update = (DistributedFieldUpdate)fieldMap.get(index);
DistributedFieldUpdate newUpdate = new DistributedFieldUpdate(val, update.getVersionId() + 1, index.intValue());
synchManager.registerUpdate(tx, this);
map.put(index, newUpdate);
txState.set(tx, map);
}
else
{
DistributedFieldUpdate newUpdate = (DistributedFieldUpdate)map.get(index);
if (newUpdate == null)
{
DistributedFieldUpdate update = (DistributedFieldUpdate)fieldMap.get(index);
newUpdate = new DistributedFieldUpdate(val, update.getVersionId() + 1, index.intValue());
map.put(index, newUpdate);
}
else
{
newUpdate.setValue(val);
}
}
}
finally
{
releaseReadLock();
}
return null;
}
public DistributedUpdate createTxUpdate(Transaction tx)
{
HashMap state = getTxState(tx);
return new DistributedPOJOUpdate(guid, state);
}
public void checkOptimisticLock(Transaction tx)
{
// NOTE THIS CODE ASSUMES THAT A WRITELOCK HAS BEEN ACQUIRED!!!!
HashMap state = getTxState(tx);
Iterator it = state.entrySet().iterator();
while (it.hasNext())
{
Map.Entry entry = (Map.Entry)it.next();
Integer index = (Integer)entry.getKey();
DistributedFieldUpdate update = (DistributedFieldUpdate)entry.getValue();
DistributedFieldUpdate orig = (DistributedFieldUpdate)fieldMap.get(index);
if (update.getVersionId() <= orig.getVersionId())
{
Advised advised = null;
if (advisedRef != null)
{
advised = (Advised)advisedRef.get();
}
if (advised != null)
{
ClassAdvisor advisor = (ClassAdvisor)advised._getAdvisor();
Field field = advisor.getAdvisedFields()[index.intValue()];
throw new OptimisticLockFailure("optimistic lock failure for field " + field.getName()
+ " of class " + field.getDeclaringClass().getName());
}
}
}
}
public void mergeState(Transaction tx) throws Exception
{
HashMap newState = getTxState(tx);
mergeState(newState);
}
public void mergeState(DistributedUpdate update) throws Exception
{
HashMap newState = ((DistributedPOJOUpdate)update).fieldUpdates;
mergeState(newState);
}
public void mergeState(HashMap newState) throws Exception
{
// NOTE THIS CODE ASSUMES THAT A WRITELOCK HAS BEEN ACQUIRED!!!!
Iterator it = newState.entrySet().iterator();
while (it.hasNext())
{
Map.Entry entry = (Map.Entry)it.next();
DistributedFieldUpdate update = (DistributedFieldUpdate)entry.getValue();
if (update.getNonDereferencedValue() instanceof VersionReference)
{
VersionReference ref = (VersionReference)update.getNonDereferencedValue();
if (ref.get() == null) ref.set((InstanceAdvised)synchManager.getObject(ref.getGUID()));
}
}
fieldMap.putAll(newState); // overwrite old state
}
public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException
{
super.writeExternal(out);
out.writeObject(classname);
out.writeObject(fieldMap);
}
public void readExternal(java.io.ObjectInput in)
throws java.io.IOException, ClassNotFoundException
{
super.readExternal(in);
this.classname = (String)in.readObject();
this.fieldMap = (HashMap)in.readObject();
try
{
InitialContext ctx = new InitialContext();
this.tm = (TransactionManager)ctx.lookup("java:/TransactionManager");
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
this.txState = new TransactionLocal();
}
}