/*
* JBoss, Home of Professional Open Source
* Copyright 2009, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2009,
* @author mark.little@jboss.com
*/
package org.jboss.stm.internal.proxy;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import org.jboss.stm.InvalidAnnotationException;
import org.jboss.stm.annotations.NotState;
import org.jboss.stm.annotations.RestoreState;
import org.jboss.stm.annotations.SaveState;
import org.jboss.stm.annotations.Transactional;
import org.jboss.stm.internal.RecoverableContainer;
import org.jboss.stm.internal.optimistic.OptimisticLockManager;
import com.arjuna.ats.arjuna.ObjectModel;
import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
import com.arjuna.ats.internal.arjuna.common.UidHelper;
public class OptimisticLockManagerProxy<T> extends OptimisticLockManager
{
public OptimisticLockManagerProxy (T candidate)
{
super();
_theObject = candidate;
_container = null;
}
public OptimisticLockManagerProxy (T candidate, RecoverableContainer<T> cont)
{
super(cont.objectType(), cont.objectModel());
_theObject = candidate;
_container = cont;
}
public OptimisticLockManagerProxy (T candidate, int ot)
{
super(ot, ObjectModel.SINGLE);
_theObject = candidate;
_container = null;
}
public OptimisticLockManagerProxy (T candidate, int ot, int om, RecoverableContainer<T> cont)
{
super(ot, om);
_theObject = candidate;
_container = cont;
}
public OptimisticLockManagerProxy (T candidate, Uid u)
{
super(u, ObjectModel.SINGLE);
_theObject = candidate;
_container = null;
}
// if there's a Uid then this is a persistent object
public OptimisticLockManagerProxy (T candidate, Uid u, RecoverableContainer<T> cont)
{
this(candidate, u, ObjectModel.SINGLE, cont);
}
public OptimisticLockManagerProxy (T candidate, Uid u, int om, RecoverableContainer<T> cont)
{
super(u, om); // TODO make configurable through annotation
_theObject = candidate;
_container = cont;
}
public synchronized boolean save_state (OutputObjectState os, int ot)
{
if (!super.save_state(os, ot))
return false;
boolean res = false;
try
{
/*
* Priority is for @SaveState and @RestoreState first.
*/
try
{
res = saveState(os);
}
catch (final InvalidAnnotationException ex)
{
ex.printStackTrace(); // TODO logging
return false;
}
if (!res) // no save_state/restore_state
{
res = true;
if (_fields == null)
{
Field[] fields = _theObject.getClass().getDeclaredFields(); // get all fields including private
_fields = new ArrayList<Field>();
try
{
for (Field afield : fields)
{
// ignore if flagged with @NotState
if (!afield.isAnnotationPresent(NotState.class) && (!THIS_NAME.equals(afield.getName())) &&
!((afield.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT))
{
_fields.add(afield);
}
}
}
catch (final Throwable ex)
{
res = false;
}
}
for (int i = 0; (i < _fields.size()) && res; i++)
{
Field afield = _fields.get(i);
synchronized (afield)
{
afield.setAccessible(true);
/*
* TODO check that the user hasn't marked statics, finals etc.
*/
if (afield.getType().isPrimitive())
{
res = packPrimitive(afield, os);
}
else
res = packObjectType(afield, os);
afield.setAccessible(false);
}
}
}
}
catch (final Throwable ex)
{
res = false;
}
return res;
}
public synchronized boolean restore_state (InputObjectState os, int ot)
{
if (!super.restore_state(os, ot))
return false;
boolean res = false;
try
{
/*
* Priority is for @SaveState and @RestoreState first.
*/
try
{
res = restoreState(os);
}
catch (final InvalidAnnotationException ex)
{
ex.printStackTrace(); // TODO logging
return false;
}
if (!res)
{
res = true;
if (_fields == null)
{
Field[] fields = _theObject.getClass().getDeclaredFields(); // get all fields including private
_fields = new ArrayList<Field>();
try
{
for (Field afield : fields)
{
// ignore if flagged with @NotState
if (!afield.isAnnotationPresent(NotState.class) && (!THIS_NAME.equals(afield.getName())) &&
!((afield.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT))
{
_fields.add(afield);
}
}
}
catch (final Throwable ex)
{
ex.printStackTrace();
res = false;
}
}
for (int i = 0; (i < _fields.size()) && res; i++)
{
Field afield = _fields.get(i);
synchronized (afield)
{
afield.setAccessible(true);
/*
* TODO check that the user hasn't marked statics, finals etc.
*/
if (afield.getType().isPrimitive())
{
res = unpackPrimitive(afield, os);
}
else
res = unpackObjectType(afield, os);
afield.setAccessible(false);
}
}
}
}
catch (final Throwable ex)
{
ex.printStackTrace();
res = false;
}
return res;
}
public String type ()
{
return "/StateManager/LockManager/OptimisticLockManager/"+_theObject.getClass().getCanonicalName();
}
public final RecoverableContainer<T> getContainer ()
{
return _container;
}
private boolean packPrimitive (final Field afield, OutputObjectState os)
{
try
{
/*
* TODO deal with arrays of primitive types.
*
* Workaround - provide saveState and restoreState annotations.
*/
if (afield.getType().equals(Boolean.TYPE))
os.packBoolean(afield.getBoolean(_theObject));
else if (afield.getType().equals(Byte.TYPE))
os.packByte(afield.getByte(_theObject));
else if (afield.getType().equals(Short.TYPE))
os.packShort(afield.getShort(_theObject));
else if (afield.getType().equals(Integer.TYPE))
os.packInt(afield.getInt(_theObject));
else if (afield.getType().equals(Long.TYPE))
os.packLong(afield.getLong(_theObject));
else if (afield.getType().equals(Float.TYPE))
os.packFloat(afield.getFloat(_theObject));
else if (afield.getType().equals(Double.TYPE))
os.packDouble(afield.getDouble(_theObject));
else if (afield.getType().equals(Character.TYPE))
os.packChar(afield.getChar(_theObject));
else
return false;
}
catch (final IOException ex)
{
return false;
}
catch (final Exception ex)
{
return false;
}
return true;
}
private boolean packObjectType (final Field afield, OutputObjectState os)
{
try
{
if (afield.getType().equals(Boolean.class))
os.packBoolean(((Boolean) afield.get(_theObject)).booleanValue());
else if (afield.getType().equals(Byte.class))
os.packByte(((Byte) afield.get(_theObject)).byteValue());
else if (afield.getType().equals(Short.class))
os.packShort(((Short) afield.get(_theObject)).shortValue());
else if (afield.getType().equals(Integer.class))
os.packInt(((Integer) afield.get(_theObject)).intValue());
else if (afield.getType().equals(Long.class))
os.packLong(((Long) afield.get(_theObject)).longValue());
else if (afield.getType().equals(Float.class))
os.packFloat(((Float) afield.get(_theObject)).floatValue());
else if (afield.getType().equals(Double.class))
os.packDouble(((Double) afield.get(_theObject)).doubleValue());
else if (afield.getType().equals(Character.class))
os.packChar(((Character) afield.get(_theObject)).charValue());
else if (afield.getType().equals(String.class))
os.packString((String) afield.get(_theObject));
else if (afield.getType().isAnnotationPresent(Transactional.class))
return packTransactionalInstance(afield, os);
else
return false;
}
catch (final Exception ex)
{
ex.printStackTrace();
return false;
}
return true;
}
/*
* This only works if this type and the types we're packing share the same container.
* So we need a way to specify (or determine) the container for all transactional
* instances.
*/
@SuppressWarnings("unchecked")
private boolean packTransactionalInstance (final Field afield, OutputObjectState os)
{
Object ptr = null;
try
{
ptr = afield.get(_theObject);
if (ptr == null)
{
os.packBoolean(false);
}
else
{
os.packBoolean(true);
UidHelper.packInto(_container.getUidForHandle((T) ptr), os);
}
}
catch (final ClassCastException ex)
{
System.err.println("Field "+ptr+" is not a transactional instance!");
return false;
}
catch (final Exception ex)
{
ex.printStackTrace();
return false;
}
return true;
}
private boolean saveState (OutputObjectState os) throws InvalidAnnotationException
{
boolean res = false;
checkValidity(_theObject.getClass());
if (_saveState != null)
{
try
{
_saveState.invoke(_theObject, os);
res = true;
}
catch (final Throwable ex)
{
ex.printStackTrace();
}
}
return res;
}
private boolean restoreState (InputObjectState os) throws InvalidAnnotationException
{
boolean res = false;
checkValidity(_theObject.getClass());
if (_restoreState != null)
{
try
{
_restoreState.invoke(_theObject, os);
res = true;
}
catch (final Throwable ex)
{
ex.printStackTrace();
}
}
return res;
}
private void checkValidity (Class<?> toCheck) throws InvalidAnnotationException
{
if (_checkSaveRestore)
return;
try
{
Method[] methods = toCheck.getDeclaredMethods();
if (methods != null)
{
for (Method mt : methods)
{
if ((mt.isAnnotationPresent(SaveState.class) && (_saveState == null)))
{
_saveState = mt;
}
if ((mt.isAnnotationPresent(RestoreState.class) && (_restoreState == null)))
{
_restoreState = mt;
}
}
}
if ((_saveState != null) && (_restoreState != null))
{
return;
}
else
{
if ((_restoreState == null) && (_saveState == null))
{
Class<?> superClass = toCheck.getSuperclass();
if (superClass != Object.class)
checkValidity(superClass);
}
else
throw new InvalidAnnotationException("WARNING: both save_state and restore_state are not present!");
}
}
finally
{
_checkSaveRestore = true;
}
}
private boolean unpackPrimitive (final Field afield, InputObjectState os)
{
try
{
// TODO arrays
if (afield.getType().equals(Boolean.TYPE))
afield.setBoolean(_theObject, os.unpackBoolean());
else if (afield.getType().equals(Byte.TYPE))
afield.setByte(_theObject, os.unpackByte());
else if (afield.getType().equals(Short.TYPE))
afield.setShort(_theObject, os.unpackShort());
else if (afield.getType().equals(Integer.TYPE))
afield.setInt(_theObject, os.unpackInt());
else if (afield.getType().equals(Long.TYPE))
afield.setLong(_theObject, os.unpackLong());
else if (afield.getType().equals(Float.TYPE))
afield.setFloat(_theObject, os.unpackFloat());
else if (afield.getType().equals(Double.TYPE))
afield.setDouble(_theObject, os.unpackDouble());
else if (afield.getType().equals(Character.TYPE))
afield.setChar(_theObject, os.unpackChar());
else
return false;
}
catch (final IOException ex)
{
ex.printStackTrace();
return false;
}
catch (final Exception ex)
{
ex.printStackTrace();
return false;
}
return true;
}
private boolean unpackObjectType (final Field afield, InputObjectState os)
{
try
{
// TODO arrays
if (afield.getType().equals(Boolean.class))
afield.set(_theObject, new Boolean(os.unpackBoolean()));
else if (afield.getType().equals(Byte.class))
afield.set(_theObject, new Byte(os.unpackByte()));
else if (afield.getType().equals(Short.class))
afield.set(_theObject, new Short(os.unpackShort()));
else if (afield.getType().equals(Integer.class))
afield.set(_theObject, new Integer(os.unpackInt()));
else if (afield.getType().equals(Long.class))
afield.set(_theObject, new Long(os.unpackLong()));
else if (afield.getType().equals(Float.class))
afield.set(_theObject, new Float(os.unpackFloat()));
else if (afield.getType().equals(Double.class))
afield.set(_theObject, new Double(os.unpackDouble()));
else if (afield.getType().equals(Character.class))
afield.set(_theObject, new Character(os.unpackChar()));
else if (afield.getType().equals(String.class))
afield.set(_theObject, os.unpackString());
else if (afield.getType().isAnnotationPresent(Transactional.class))
return unpackTransactionalInstance(afield, os);
else
return false;
}
catch (final IOException ex)
{
ex.printStackTrace();
return false;
}
catch (final Exception ex)
{
ex.printStackTrace();
return false;
}
return true;
}
/*
* This only works if this type and the types we're packing share the same container.
* So we need a way to specify (or determine) the container for all transactional
* instances.
*/
private boolean unpackTransactionalInstance (final Field afield, InputObjectState os)
{
try
{
boolean ptr = os.unpackBoolean();
if (!ptr)
afield.set(_theObject, null);
else
{
Uid u = UidHelper.unpackFrom(os);
afield.set(_theObject, _container.getHandle(u));
}
}
catch (final Exception ex)
{
ex.printStackTrace();
return false;
}
return true;
}
// the object we are working on.
private T _theObject;
// the cached methods/fields
private boolean _checkSaveRestore = false;
private Method _saveState = null;
private Method _restoreState = null;
private RecoverableContainer<T> _container = null;
private ArrayList<Field> _fields = null;
private static final String THIS_NAME = "this$0"; // stop us trying to pack this!
}