/*
* 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;
import java.security.InvalidParameterException;
import java.util.Iterator;
import java.util.Set;
import java.util.WeakHashMap;
import org.jboss.stm.internal.PersistentContainer;
import org.jboss.stm.internal.RecoverableContainer;
import org.jboss.stm.internal.proxy.LockManagerProxy;
import org.jboss.stm.internal.proxy.OptimisticLockManagerProxy;
import com.arjuna.ats.arjuna.ObjectModel;
import com.arjuna.ats.arjuna.ObjectType;
import com.arjuna.ats.arjuna.common.Uid;
/**
* Instances of this class represent the transactional memory within which user objects
* can be placed and managed.
*
* Objects must implement an interface through which all transactional accesses occur. We don't
* mandate what the interface is, since that will depend upon the business logic. The interface, or
* the implementing class, must also use the @Transactional annotation.
*
* Unless either the Nested or NestedTopLevel annotation is used, all method invocations on objects returned from a Container
* should be done within the context of an active transaction.
*
* @author marklittle
*/
public class Container<T>
{
/**
* The TYPE of the objects created by this instance.
*
* RECOVERABLE cannot be shared between address spaces and cannot tolerate crash failures.
* PERSISTENT can be shared between address spaces (though don't have to be) and can tolerate crash failures.
*
* @author marklittle
*/
public enum TYPE { RECOVERABLE, PERSISTENT };
/**
* The sharing MODEL of the objects created by this instance.
*
* SHARED means the instance may be used within multiple processes. It must be PERSISTENT too.
* EXCLUSIVE means that the instance will only be used within a single process. It can be PERSISTENT or RECOVERABLE.
*
* @author marklittle
*/
public enum MODEL { SHARED, EXCLUSIVE };
/**
* Create a container without a name. A name will be assigned automatically.
*/
public Container ()
{
this(new Uid().stringForm(), TYPE.RECOVERABLE);
}
/**
* Create a container (system assigned name) of the specified type. Objects will be EXCLUSIVE.
*
* @param type the type of objects created.
*/
public Container (final TYPE type)
{
this(new Uid().stringForm(), type);
}
/**
* Create a container (system assigned name) of the specified type and model.
*
* @param type the TYPE of objects.
* @param model the MODEL of the objects.
*/
public Container (final TYPE type, final MODEL model)
{
this(new Uid().stringForm(), type, model);
}
/**
* Create a named container. Objects will be RECOVERABLE and EXCLUSIVE.
*
* @param name the name (should be unique, but this is not enforced).
*/
public Container (final String name)
{
this(name, TYPE.RECOVERABLE);
}
/**
* Create a named container. Objects will be EXCLUSIVE.
*
* @param name the name (should be unique, but this is not enforced).
* @param type the TYPE of objects.
*/
public Container (final String name, final TYPE type)
{
if (type == TYPE.RECOVERABLE)
_theContainer = new RecoverableContainer<T>(name);
else
_theContainer = new PersistentContainer<T>(name);
}
/**
* Create a named container.
*
* @param name the name (should be unique, but this is not enforced).
* @param type the TYPE of objects.
* @param model the MODEL of objects.
*/
public Container (final String name, final TYPE type, final MODEL model)
{
int theModel = (model == MODEL.SHARED ? ObjectModel.MULTIPLE : ObjectModel.SINGLE);
if (type == TYPE.RECOVERABLE)
{
if (model != MODEL.EXCLUSIVE)
throw new InvalidParameterException("Object must be EXCLUSIVE!");
_theContainer = new RecoverableContainer<T>(name); // NOTE currently ObjectModel data not exposed for RecoverableContainers
}
else
_theContainer = new PersistentContainer<T>(name, theModel);
}
/**
* Get the name of the container.
*
* @return the name.
*/
public final String name ()
{
return _theContainer.name();
}
/**
* @return the TYPE of objects created by this instance.
*/
public final TYPE type ()
{
if (_theContainer.objectType() == ObjectType.RECOVERABLE)
return TYPE.RECOVERABLE;
else
return TYPE.PERSISTENT;
}
/**
* @return the MODEL of the objects created by this instance.
*/
public final MODEL model ()
{
if (_theContainer.objectModel() == ObjectModel.MULTIPLE)
return MODEL.SHARED;
else
return MODEL.EXCLUSIVE;
}
/**
* Given an object we create a new transactional instance of it and return that
* for future use. All accesses on the returned object will be managed according to
* the rules defined in the various annotations. If the original object instance is used
* then no transactional manipulation will occur so you need to be careful!
*
* All handles are uniquely identified using Uid.
*
* @param member the instance of type T that you want to be made transactional and persistent.
* @return a handle into the transactional memory that the application should use to manipulate the object.
*/
public synchronized T create (T member)
{
return _theContainer.enlist(member);
}
/**
* Given an existing object, create another handle. This is particularly
* useful when using pessimistic concurrency control and we need one object instance per
* thread to ensure that state is safely managed.
*
* @param member the instance of type T that you want to be made transactional and persistent.
* @param proxy the instance you want to copy.
*/
public synchronized T clone (T member, T proxy)
{
if (member == null)
throw new InvalidParameterException();
/*
* If we are using pessimistic cc then we don't need to do a clone and can
* return the same instance. No MVCC needed here, so shortcut.
*/
if (_theContainer.isPessimistic(proxy))
return proxy;
else
return _theContainer.enlist(member, _theContainer.getUidForHandle(proxy));
}
/**
* Given an identified for an existing object, create another handle. This is particularly
* useful when using optimistic concurrency control and we need one object instance per
* thread to ensure that state is safely managed.
*
* WARNING: if the Uid is invalid, e.g., points to a state that no longer exists, then a handle
* will still be returned because checks for validity (other than null parameter) cannot be done
* until you try to use the state. At that time a lock will be refused (state cannot be activated)
* and a suitable warning message will be output.
*
* @param member the instance of type T that you want to be made transactional and persistent.
* @param id the Uid of the object.
*/
public synchronized T clone (T member, Uid id)
{
if (member == null)
throw new InvalidParameterException();
return _theContainer.enlist(member, id);
}
/**
* @return the unique name for the instance.
*/
public Uid getIdentifier (T proxy)
{
return _theContainer.getUidForHandle(proxy);
}
/**
* Given the proxy return the container that is managing it.
*
* @param proxy the instance within the container we're looking for.
* @return the container or null. Shouldn't really be possible to get null!
*/
public static final Container<?> getContainer (Object proxy)
{
/*
* Rather than maintain a list of Container instances and iterate through them
* we create a clone of the Container using the real container within the
* proxy itself. Container is essentially a (almost) stateless wrapper class
* anyway so hopefully this is a lightweight and faster way of achieving this.
* Can revisit later if this creates too many instances.
*/
Container<?> toReturn = null;
if (proxy instanceof OptimisticLockManagerProxy<?>)
{
RecoverableContainer<?> cont = ((OptimisticLockManagerProxy<?>) proxy).getContainer();
toReturn = new Container(cont);
}
else
{
if (proxy instanceof LockManagerProxy<?>)
{
RecoverableContainer<?> cont = ((LockManagerProxy<?>) proxy).getContainer();
toReturn = new Container(cont);
}
else
throw new IllegalArgumentException("Not a proxy object!");
}
return toReturn;
}
private Container (RecoverableContainer<T> toUse)
{
_theContainer = toUse;
}
/*
* The actual container (recoverable or persistent).
*/
private RecoverableContainer<T> _theContainer;
}