/*
* 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.proxy.ejb.handle;
import java.rmi.RemoteException;
import java.rmi.ServerException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Hashtable;
import javax.ejb.Handle;
import javax.ejb.EJBObject;
import javax.naming.InitialContext;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.InvocationContext;
import org.jboss.invocation.InvocationKey;
import org.jboss.invocation.InvocationType;
import org.jboss.invocation.Invoker;
import org.jboss.invocation.InvokerInterceptor;
import org.jboss.invocation.PayloadKey;
import org.jboss.logging.Logger;
import org.jboss.naming.NamingContextFactory;
import org.jboss.security.SecurityAssociation;
/**
* An EJB stateful session bean handle.
*
* @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @author <a href="bill@burkecentral.com">Bill Burke</a>
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
* @version $Revision: 84397 $
*/
public class StatefulHandleImpl
implements Handle
{
private static final Logger log = Logger.getLogger(StatefulHandleImpl.class);
/** Serial Version Identifier. */
static final long serialVersionUID = -6324520755180597156L;
/** A reference to {@link Handle#getEJBObject}. */
protected static final Method GET_EJB_OBJECT;
/** The value of our local Invoker.ID to detect when we are local. */
private Object invokerID = null;
/**
* Initialize <tt>Handle</tt> method references.
*/
static
{
try
{
GET_EJB_OBJECT = Handle.class.getMethod("getEJBObject", new Class[0]);
}
catch(Exception e)
{
e.printStackTrace();
throw new ExceptionInInitializerError(e);
}
}
/** The identity of the bean. */
public int objectName;
public String jndiName;
public String invokerProxyBinding;
public Invoker invoker;
public Object id;
/** The JNDI env in effect when the home handle was created */
protected Hashtable jndiEnv;
public StatefulHandleImpl()
{
}
/** Create an ejb handle for a stateful session bean.
* @param objectName - the session container jmx name
* @param jndiName - the session home ejb name
* @param invoker - the invoker to request the EJBObject from
* @param invokerProxyBinding - the type of invoker binding
* @param id - the session id
*/
public StatefulHandleImpl(
int objectName,
String jndiName,
Invoker invoker,
String invokerProxyBinding,
Object id,
Object invokerID)
{
this.jndiName = jndiName;
this.id = id;
this.jndiEnv = (Hashtable) NamingContextFactory.lastInitialContextEnv.get();
try
{
String property = System.getProperty("org.jboss.ejb.sfsb.handle.V327");
if (property != null)
{
this.invokerProxyBinding = invokerProxyBinding;
this.invokerID = invokerID;
this.objectName = objectName;
this.invoker = invoker;
}
}
catch (AccessControlException ignored)
{
}
}
/**
* @return the internal session identifier
*/
public Object getID()
{
return id;
}
/**
* @return the jndi name
*/
public String getJNDIName()
{
return jndiName;
}
/**
* Handle implementation.
*
* This differs from Stateless and Entity handles which just invoke
* standard methods (<tt>create</tt> and <tt>findByPrimaryKey</tt>
* respectively) on the Home interface (proxy).
* There is no equivalent option for stateful SBs, so a direct invocation
* on the container has to be made to locate the bean by its id (the
* stateful SB container provides an implementation of
* <tt>getEJBObject</tt>).
*
* This means the security context has to be set here just as it would
* be in the Proxy.
*
* @return <tt>EJBObject</tt> reference.
*
* @throws ServerException Could not get EJBObject.
*/
public EJBObject getEJBObject() throws RemoteException
{
if (invokerProxyBinding != null)
{
try
{
return getEjbObjectViaInvoker();
}
catch(Exception e)
{
log.debug("Exception reported, try JNDI method to recover EJB object instead", e);
return getEjbObjectViaJndi();
}
}
return getEjbObjectViaJndi();
}
/**
* Returns wether we are local to the originating container or not.
*/
protected boolean isLocal()
{
return invokerID != null && invokerID.equals(Invoker.ID);
}
protected EJBObject getEjbObjectViaInvoker() throws Exception
{
if (log.isTraceEnabled())
{
log.trace("Using legacy invoker method for getEJBObject() invocation.");
}
SecurityActions sa = SecurityActions.UTIL.getSecurityActions();
Invocation invocation = new Invocation(
null,
GET_EJB_OBJECT,
new Object[]{id},
//No transaction set up in here? it will get picked up in the proxy
null,
// fix for bug 474134 from Luke Taylor
sa.getPrincipal(),
sa.getCredential());
invocation.setObjectName(new Integer(objectName));
invocation.setValue(InvocationKey.INVOKER_PROXY_BINDING,
invokerProxyBinding, PayloadKey.AS_IS);
// It is a home invocation
invocation.setType(InvocationType.HOME);
// Create an invocation context for the invocation
InvocationContext ctx = new InvocationContext();
invocation.setInvocationContext(ctx);
// Get the invoker to the target server (cluster or node)
// Ship it
if (isLocal())
return (EJBObject) InvokerInterceptor.getLocal().invoke(invocation);
else
return (EJBObject) invoker.invoke(invocation);
}
protected EJBObject getEjbObjectViaJndi() throws RemoteException
{
try
{
if (log.isTraceEnabled())
{
log.trace("Using JNDI method for getEJBObject() invocation.");
}
InitialContext ic = null;
if( jndiEnv != null )
ic = new InitialContext(jndiEnv);
else
ic = new InitialContext();
Proxy proxy = (Proxy) ic.lookup(jndiName);
// call findByPrimary on the target
InvocationHandler ih = Proxy.getInvocationHandler(proxy);
return (EJBObject) ih.invoke(proxy, GET_EJB_OBJECT, new Object[] {id});
}
catch (RemoteException e)
{
throw e;
}
catch (Throwable t)
{
t.printStackTrace();
throw new RemoteException("Error during getEJBObject", t);
}
}
interface SecurityActions
{
class UTIL
{
static SecurityActions getSecurityActions()
{
return System.getSecurityManager() == null ? NON_PRIVILEGED : PRIVILEGED;
}
}
SecurityActions NON_PRIVILEGED = new SecurityActions()
{
public Principal getPrincipal()
{
return SecurityAssociation.getPrincipal();
}
public Object getCredential()
{
return SecurityAssociation.getCredential();
}
};
SecurityActions PRIVILEGED = new SecurityActions()
{
private final PrivilegedAction getPrincipalAction = new PrivilegedAction()
{
public Object run()
{
return SecurityAssociation.getPrincipal();
}
};
private final PrivilegedAction getCredentialAction = new PrivilegedAction()
{
public Object run()
{
return SecurityAssociation.getCredential();
}
};
public Principal getPrincipal()
{
return (Principal)AccessController.doPrivileged(getPrincipalAction);
}
public Object getCredential()
{
return AccessController.doPrivileged(getCredentialAction);
}
};
Principal getPrincipal();
Object getCredential();
}
}