/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt 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.
*/
/*
* Created on Jul 14, 2004
*
* The source code contained in this file is in in the public domain.
* It can be used in any project, or product without prior permission,
* license or royalty payments. There is no claim of correctness and
* NO WARRANTY OF ANY KIND provided with this code.
*/
package org.mobicents.slee.runtime.sbbentity;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.slee.CreateException;
import javax.slee.SLEEException;
import javax.slee.SbbLocalObject;
import javax.slee.TransactionRequiredLocalException;
import javax.transaction.SystemException;
import org.apache.log4j.Logger;
import org.mobicents.slee.SbbLocalObjectExt;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.component.sbb.GetChildRelationMethodDescriptor;
import org.mobicents.slee.container.sbbentity.ChildRelation;
import org.mobicents.slee.container.sbbentity.SbbEntity;
import org.mobicents.slee.container.sbbentity.SbbEntityID;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.runtime.sbb.SbbLocalObjectImpl;
/**
*
* Implements the ChildRelation interface
*
* @author F.Moggia
* @author M. Ranganathan
* @author Ralf Siedow
* @author eduardomartins
*
*
*/
public class ChildRelationImpl implements ChildRelation {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(ChildRelationImpl.class);
private static final SleeContainer sleeContainer = SleeContainer.lookupFromJndi();
private GetChildRelationMethodDescriptor getChildRelationMethod;
private SbbEntityImpl sbbEntity;
private HashSet<SbbLocalObject> getLocalObjects() {
HashSet<SbbLocalObject> localObjects = new HashSet<SbbLocalObject>();
SbbEntity childSbbEntity = null;
for (SbbEntityID sbbEntityId : sbbEntity.cacheData.getChildRelationSbbEntities(getChildRelationMethod.getChildRelationMethodName())) {
childSbbEntity = sleeContainer.getSbbEntityFactory().getSbbEntity(sbbEntityId, false);
if (childSbbEntity != null) {
localObjects.add(childSbbEntity.getSbbLocalObject());
}
}
return localObjects;
}
/**
* Creates a new instance of a child relation
* @param getChildRelationMethod the child relation method
* @param sbbEntity the sbb entity that owns this child relation
*/
public ChildRelationImpl(GetChildRelationMethodDescriptor getChildRelationMethod,
SbbEntityImpl sbbEntity) {
if (getChildRelationMethod == null)
throw new NullPointerException("null getChildRelationMethod");
this.sbbEntity = sbbEntity;
this.getChildRelationMethod = getChildRelationMethod;
}
@SuppressWarnings("rawtypes")
public Iterator iterator() {
return new ChildRelationIterator();
}
/**
* The contains method. This method returns true if the SBB entity
* represented by the SBB local object specified by the input argument is a
* member of this child relation. If the method argument is not an SBB local
* object, is an invalid SBB local object, or is an SBB local object whose
* underlying SBB entity is not a member of this child relation, then this
* method returns false.
*
*/
public boolean contains(Object object) {
if (!(object instanceof SbbLocalObject))
return false;
final SbbLocalObjectImpl sbblocal = (SbbLocalObjectImpl) object;
final SbbEntityID sbbEntityId = sbblocal.getSbbEntityId();
if(!idBelongsToChildRelation(sbbEntityId)) {
return false;
}
return new SbbEntityCacheData(sbbEntityId,sleeContainer.getCluster().getMobicentsCache()).exists();
}
private boolean idBelongsToChildRelation(SbbEntityID sbbEntityID) {
if(sbbEntityID.isRootSbbEntity()) {
return false;
}
if(!sbbEntityID.getParentChildRelation().equals(getChildRelationMethod.getChildRelationMethodName())) {
return false;
}
if(!sbbEntityID.getParentSBBEntityID().equals(sbbEntity.getSbbEntityId())) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see javax.slee.ChildRelation#create()
*/
public SbbLocalObject create() throws CreateException,
TransactionRequiredLocalException, SLEEException {
return create(sleeContainer.getUuidGenerator().createUUID());
}
/*
* (non-Javadoc)
*
* @see java.util.Collection#size()
*/
public int size() {
return sbbEntity.cacheData.getChildRelationSbbEntities(getChildRelationMethod.getChildRelationMethodName()).size();
}
/*
* (non-Javadoc)
*
* @see java.util.Collection#clear()
*/
@SuppressWarnings("rawtypes")
public void clear() {
sleeContainer.getTransactionManager().mandateTransaction();
for (Iterator it = iterator();it.hasNext();) {
it.next();
it.remove();
}
}
/*
* (non-Javadoc)
*
* @see java.util.Collection#isEmpty()
*/
public boolean isEmpty() {
return size() == 0;
}
/*
* (non-Javadoc)
*
* @see java.util.Collection#toArray()
*/
public Object[] toArray() {
sleeContainer.getTransactionManager().mandateTransaction();
return this.getLocalObjects().toArray();
}
/**
* This operation is not supported ( see page 62 ) of the spec. The add
* methods add and addAll. Any attempts to add to the child relation using
* these methods result in a java.lang. UnsupportedOperationException. For
* example, the SLEE must throw the UnsupportedOperationException when these
* methods are invoked with arguments that add to the collection. o An
* invocation of an add method that has no effect on the collection, such as
* invoking the addAll method with an empty collection may or may not throw
* an UnsupportedOperationException . The create method of the ChildRelation
* interface should be used to create a child SBB entity. This causes the
* SLEE to automatically add an SBB local object that represents the newly
* created SBB entity to the collection.
*/
public boolean add(Object object) {
if (object == null)
throw new NullPointerException("null arg! ");
else
throw new UnsupportedOperationException("Operation not supported !");
}
/**
* Spec page 62 The remove methods: clear, remove, removeAll, and retainAll.
* o These methods may remove SBB entities from the child relation. The
* input argument specifies which SBB entities will be removed from the
* child relation or retained in the child relation by specifying the SBB
* local object or collection of SBB local objects that represent the SBB
* entities to be removed or retained. o Removing an SBB entity from a child
* relation initiates a cascading removal of the SBB entity tree rooted by
* the SBB entity, similar to invoking the remove method on an SBB local
* object that represents the SBB entity.
*
*/
public boolean remove(Object object) {
sleeContainer.getTransactionManager().mandateTransaction();
if(logger.isDebugEnabled()) {
logger.debug("removing sbb local object " + object);
}
if (object == null)
throw new NullPointerException("Null arg for remove ");
if (!(object instanceof SbbLocalObject))
return false;
final SbbLocalObjectImpl sbbLocalObjectImpl = (SbbLocalObjectImpl) object;
if(!idBelongsToChildRelation(sbbLocalObjectImpl.getSbbEntityId()) && !sbbLocalObjectImpl.getSbbEntity().isRemoved()) {
return false;
}
else {
sbbLocalObjectImpl.remove();
return true;
}
}
/**
* This operation is not supported ( see page 62 ) of the spec. The add
* methods add and addAll. Any attempts to add to the child relation using
* these methods result in a java.lang. UnsupportedOperationException. For
* example, the SLEE must throw the UnsupportedOperationException when these
* methods are invoked with arguments that add to the collection. o An
* invocation of an add method that has no effect on the collection, such as
* invoking the addAll method with an empty collection may or may not throw
* an UnsupportedOperationException . The create method of the ChildRelation
* interface should be used to create a child SBB entity. This causes the
* SLEE to automatically add an SBB local object that represents the newly
* created SBB entity to the collection.
*/
@SuppressWarnings("rawtypes")
public boolean addAll(Collection c) {
if (c == null)
throw new NullPointerException("Null arg!");
else
throw new UnsupportedOperationException("Operation not supported !");
}
/**
* This method returns true if all SBB entities represented by the SBB local
* objects in the collection specified by the input argument are members of
* this child relation. If the collection contains an object that is not an
* SBB local object, an SBB local object that is invalid, or an SBB local
* object whose underlying SBB entity is not a member of this child
* relation, then this method returns false.
*/
@SuppressWarnings("rawtypes")
public boolean containsAll(Collection c) {
if (c == null)
throw new NullPointerException("null collection!");
for (Iterator it = c.iterator(); it.hasNext(); ) {
if (!contains(it.next())) {
return false;
}
}
if(logger.isDebugEnabled()) {
logger.debug("containsAll : collection = " + c + " > all in child relation");
}
return true;
}
/**
* Removing an SBB entity from a child relation initiates a cascading
* removal of the SBB entity tree rooted by the SBB entity, similar to
* invoking the remove method on an SBB local object that represents the SBB
* entity.
*
*/
@SuppressWarnings("rawtypes")
public boolean removeAll(Collection c) {
boolean flag = true;
if (c == null)
throw new NullPointerException(" null collection ! ");
for ( Iterator it = c.iterator(); it.hasNext(); ) {
flag &= this.remove( it.next());
}
return flag;
}
/*
* (non-Javadoc)
*
* @see java.util.Collection#retainAll(java.util.Collection)
*/
@SuppressWarnings("rawtypes")
public boolean retainAll(Collection c) {
boolean flag = false;
if (c == null)
throw new NullPointerException(" null arg! ");
for ( Iterator it = this.iterator(); it.hasNext();) {
if ( ! c.contains(it.next())) {
flag = true;
it.remove();
}
}
return flag;
}
/*
* (non-Javadoc)
*
* @see java.util.Collection#toArray(java.lang.Object[])
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object[] toArray(Object[] a) {
if (a == null)
throw new NullPointerException("null arg!");
HashSet localObjects = this.getLocalObjects();
return localObjects.toArray(a);
}
public Set<SbbEntityID> getSbbEntitySet(){
return new HashSet<SbbEntityID>(sbbEntity.cacheData.getChildRelationSbbEntities(getChildRelationMethod.getChildRelationMethodName()));
}
// --- ITERATOR
/*
* JAIN SLEE 1.0 Specification, Final Release Page 62 The iterator method
* and the Iterator objects returned by this method. Chapter 6 The SBB
* Abstract Class
*
* The SLEE must implement the methods of the Iterator objects returned by
* the iterator method by following the contract defined by the
* java.util.Iterator interface. o The next method of the Iterator object
* returns an object that implements the SBB local interface of the child
* SBB of the child relation. Java typecast can be used to narrow the
* returned object reference to the SBB local interface of the child SBB. o
* The remove method of the Iterator object removes the SBB entity that is
* represented by the last SBB local object returned by the next method of
* the Iterator object from the child relation. Removing an SBB entity from
* a child relation by invoking this remove method initiates a cascading
* removal of the SBB entity tree rooted by the SBB entity, similar to
* invoking the remove method on an SBB local object that represents the SBB
* entity. The behavior of the Iterator object is unspecified if the
* underlying child relation is modified while the iteration is in progress
* in any way other than by calling this remove method.
*/
@SuppressWarnings("rawtypes")
class ChildRelationIterator implements Iterator {
private Iterator<SbbEntityID> myIterator;
private SbbEntityID nextEntity;
private SbbLocalObject nextSbbLocalObject;
public ChildRelationIterator() {
this.myIterator = getSbbEntitySet().iterator();
}
public void remove() {
if (nextSbbLocalObject != null) {
myIterator.remove();
nextSbbLocalObject.remove();
}
else {
throw new IllegalStateException();
}
}
public boolean hasNext() {
return myIterator.hasNext();
}
public Object next() {
nextEntity = myIterator.next();
SbbEntity childSbbEntity = sleeContainer.getSbbEntityFactory().getSbbEntity(nextEntity, false);
if (childSbbEntity != null) {
this.nextSbbLocalObject = childSbbEntity.getSbbLocalObject();
sbbEntity.addChildWithSbbObject(childSbbEntity);
return nextSbbLocalObject;
}
else {
return next();
}
}
}
// extension methods
private void validateChildName(String childName) throws IllegalArgumentException, NullPointerException {
if (childName == null) {
throw new NullPointerException("null child name");
}
if (childName.isEmpty()) {
throw new IllegalArgumentException("empty child name");
}
}
@Override
public SbbLocalObjectExt create(String childName) throws CreateException,
IllegalArgumentException, NullPointerException,
TransactionRequiredLocalException, SLEEException {
if (logger.isDebugEnabled())
logger.debug("Creating child "+childName+" from relation "+getChildRelationMethod.getChildRelationMethodName()+" of entity "+sbbEntity.getSbbEntityId());
validateChildName(childName);
final SleeTransactionManager sleeTransactionManager = sleeContainer
.getTransactionManager();
sleeTransactionManager.mandateTransaction();
final SbbEntity childSbbEntity = sleeContainer.getSbbEntityFactory()
.createNonRootSbbEntity(sbbEntity.getSbbEntityId(),
getChildRelationMethod.getChildRelationMethodName(),
childName);
if (logger.isDebugEnabled())
logger.debug("Created child Sbb Entity: "
+ childSbbEntity.getSbbEntityId());
childSbbEntity.setPriority(getChildRelationMethod.getDefaultPriority());
/*
* Exception handling here must follow Sec. 9.12.1 of spec This is a
* non-slee originated method invocation
*
* If a non-SLEE originated method invocation returns by throwing a
* checked exception, then the following occurs: � The state of the
* transaction is unaffected � The checked exception is propagated to
* the caller. It is expected that the caller will have the appropriate
* logic to handle the exception. If a non-SLEE originated method
* invocation returns by throwing a RuntimeException, then: � The
* transaction is marked for rollback. � The SBB object that was invoked
* is discarded, i.e. is moved to the Does Not Exist state. � A
* javax.slee.TransactionRolledBackLocalException is propagated to the
* caller. The transaction will eventually be rolled back when the
* highest level SLEE originated invocation re-turns as described in
* Section 9.12.2. The sbbRolledBack method is not invoked for an SBB
* originated method transaction because the transa ction is only marked
* for rollback and will only be rolled back when the highest level SLEE
* originated invocation returns. The sbbRolledBack method is only
* invoked on the SBB entity in-voked by the highest level SLEE
* originated invocation. If the RuntimeException propagates to the
* highest level (i.e. the SLEE originated method invocation returns by
* throwing a RuntimeException) the SLEE originated method invocation
* exception handling mechanism is init iated.
*/
if (System.getSecurityManager()!=null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws CreateException {
assignSbbObjectToChild(childSbbEntity,sleeTransactionManager);
return null;
}
});
}
catch (PrivilegedActionException e) {
final Throwable t = e.getCause();
if (t instanceof CreateException) {
throw (CreateException) t;
}
else {
throw new SLEEException(t.getMessage(),t);
}
}
}
else {
assignSbbObjectToChild(childSbbEntity,sleeTransactionManager);
}
sbbEntity.addChildWithSbbObject(childSbbEntity);
return childSbbEntity.getSbbLocalObject();
}
private void assignSbbObjectToChild(final SbbEntity childSbbEntity, final SleeTransactionManager sleeTransactionManager) throws CreateException {
final Thread t = Thread.currentThread();
final ClassLoader cl = t.getContextClassLoader();
t.setContextClassLoader(childSbbEntity.getSbbComponent().getClassLoader());
try {
// All checked exceptions (i.e. CreateException) are propagated to
// the caller
childSbbEntity.assignSbbObject();
} catch (CreateException e) {
// All RuntimeExceptions are dealt with here
if (logger.isDebugEnabled())
logger.error("Caught CreateException in creating child entity",
e);
childSbbEntity.trashObject();
// Propagate exception to caller.
throw e;
} catch (Exception e) {
// All RuntimeExceptions are dealt with here
logger.error("Caught RuntimeException in creating child entity", e);
try {
sleeTransactionManager.setRollbackOnly();
} catch (SystemException e1) {
logger.error("Failed to set rollbackonly", e);
}
childSbbEntity.trashObject();
}
finally {
t.setContextClassLoader(cl);
}
}
@Override
public SbbLocalObjectExt get(String childName)
throws IllegalArgumentException, NullPointerException,
TransactionRequiredLocalException, SLEEException {
if (logger.isTraceEnabled())
logger.trace("Retreiving child "+childName+" from relation "+getChildRelationMethod.getChildRelationMethodName()+" of entity "+sbbEntity.getSbbEntityId());
validateChildName(childName);
SleeTransactionManager sleeTransactionManager = sleeContainer
.getTransactionManager();
sleeTransactionManager.mandateTransaction();
SbbEntity childSbbEntity = sleeContainer.getSbbEntityFactory()
.getSbbEntity(
new NonRootSbbEntityID(sbbEntity.getSbbEntityId(),
getChildRelationMethod
.getChildRelationMethodName(),
childName), false);
if (logger.isDebugEnabled())
logger.debug("Retrieved child "+childName+" from relation "+getChildRelationMethod.getChildRelationMethodName()+" of entity "+sbbEntity.getSbbEntityId()+" -> "+childSbbEntity);
return childSbbEntity == null ? null : childSbbEntity
.getSbbLocalObject();
}
}