-
1. Re: How to mark SLSB as unavailable?
anguyen Mar 30, 2005 1:18 PM (in response to anguyen)Forgot to mention, I'm using JBoss 3.2.6 on Linux JDK 1.4.2_07.
Looking at JRMPInvokerProxyHA.invoke(), it looks like the invocation must throw one of the following exceptions in order for fail-over to happen: java.net.ConnectException, java.net.UnknownHostException, java.rmi.ConnectException, java.rmi.ConnectIOException, java.rmi.NoSuchObjectException, java.rmi.UnmarshalException, java.rmi.UnknownHostException or GenericClusteringException (with completionStatus == GenericClusteringException.COMPLETED_NO). It doesn't look my EJB can throw any of these exceptions directly, so my question then would be, what can I do in my EJB to make the container or one of the interceptors throw one of these exceptions? Do I need to write my own interceptor? Should I modify JRMPInvokerProxyHA.invoke() to catch EJBException and failover when this happens?
Andy Nguyen -
2. Re: How to mark SLSB as unavailable?
anguyen Apr 2, 2005 9:40 PM (in response to anguyen)I guess I'm alone in this.
I've done some more digging and found this post http://www.jboss.org/index.html?module=bb&op=viewtopic&t=61675 and this bug http://jira.jboss.com/jira/browse/JBAS-1600 which says it has been fixed in 4.0.2. I haven't checked to see what the behavior is now in 4.0.2, but I'm hoping that if a CreateException is thrown from ejbCreate, the behavior of the client proxy will be to fail-over.
I will look into 4.0.2 and see what happens. Hopefully, I can back port this fix to 3.2.6. -
3. Re: How to mark SLSB as unavailable?
anguyen Apr 6, 2005 1:55 PM (in response to anguyen)Well, it looks like I found a solution to my problem.
Problem: I have a stateless session EJB that throws a CreateException in ejbCreate(). In a clustered environment, I believe this should cause the client proxy to fail-over to another node. The current behavior in JBoss 3.2.6 is to throw the CreateException back to the client wrapped in an UndeclaredThrowableException. The client proxy just propogates the UndeclaredThrowableException all the way back up the call chain without failing over.
Solution: I wrote my own interceptor that catches CreateExceptions and throws back a GenericClusteringException. The client proxy will fail-over when it receives a GenericClusteringException with status set to not completed.
Here is the code to my interceptor, in case anyone is interested.package com.itgssi.util.jboss.ejb; import javax.ejb.CreateException; import org.jboss.ejb.plugins.AbstractInterceptor; import org.jboss.ha.framework.interfaces.GenericClusteringException; import org.jboss.invocation.Invocation; /** * <p> * <code>CreateExceptionHandlerInterceptor</code> handles CreateExceptions * during invocations to an EJB. In order for these exceptions to cause the * client to fail-over in a clustered environment, they get converted to * <code>GenericClusteringException</code>s. * </p> * <p> * <em>NOTE:</em> for this interceptor to work propertly, it must come BEFORE * <code>org.jboss.ejb.plugins.CleanShutdownInterceptor</code>. * <code>CleanShutdownInterceptor</code> will change the status on * <code>GenericClusteringException</code> s that pass through it to MAYBE, * which will not cause the client to fail-over. This is the behavior in JBoss * 3.2.6; I'm not sure about other versions. * </p> * * @author anguyen */ public class CreateExceptionHandlerInterceptor extends AbstractInterceptor { /** * This invocation handles <code>CreateException</code>s thrown by * other invokers further down the chain. The current behavior is * to log the <code>CreateException</code>'s message as a warning * and throw a <code>GenericClusteringException</code> with a * completed status of NO. Clients of a clustered EJB should * interpret this exception as an indication that the EJB * is unavailable on this node and fail-over to another node. */ public Object invoke(Invocation mi) throws Exception { try { return getNext().invoke(mi); } catch (CreateException e) { handleCreateException(e); // if handle create exception doesn't throw an exception.... throw e; } catch (Exception e) { Throwable rootCause = e; while ((rootCause = rootCause.getCause()) != null) { // loop until we get to the root cause } if (rootCause instanceof CreateException) { handleCreateException((CreateException) rootCause); } // if the root cause is not a CreateException, rethrow // the original exception throw e; } } void handleCreateException(CreateException e) throws Exception { String message = "EJB creation failed on node"; log.warn(message); log.debug("Stack Trace", e); throw new GenericClusteringException(GenericClusteringException.COMPLETED_NO, message); } }
To use this interceptor, I created a custom container for my EJB in jboss.xml.<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_3_0.dtd"> <jboss> <enterprise-beans> <!-- To add beans that you have deployment descriptor info for, add a file to your XDoclet merge directory called jboss-beans.xml that contains the <session></session>, <entity></entity> and <message-driven></message-driven> markup for those beans. --> <session> <ejb-name>MySLSB</ejb-name> <jndi-name>ejb/MySLSB</jndi-name> <configuration-name>My Clustered Stateless SessionBean</configuration-name> <clustered>true</clustered> <cluster-config> <partition-name>DefaultPartition</partition-name> <home-load-balance-policy>org.jboss.ha.framework.interfaces.RoundRobin</home-load-balance-policy> <bean-load-balance-policy>org.jboss.ha.framework.interfaces.RoundRobin</bean-load-balance-policy> </cluster-config> </session> </enterprise-beans> <resource-managers> </resource-managers> <!-- | for container settings, you can merge in jboss-container.xml | this can contain <invoker-proxy-bindings/> and <container-configurations/> --> <container-configurations> <container-configuration> <container-name>My Clustered Stateless SessionBean</container-name> <call-logging>false</call-logging> <invoker-proxy-binding-name>clustered-stateless-rmi-invoker</invoker-proxy-binding-name> <container-interceptors> <!-- This interceptor must come before CleanShutdownInterceptor to work properly --> <interceptor>com.itgssi.util.jboss.ejb.CreateExceptionHandlerInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.CleanShutdownInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.LogInterceptor</interceptor> <interceptor>org.jboss.ejb.plugins.SecurityInterceptor</interceptor> <!-- CMT --> <interceptor transaction="Container">org.jboss.ejb.plugins.TxInterceptorCMT</interceptor> <interceptor transaction="Container" metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor> <interceptor transaction="Container">org.jboss.ejb.plugins.StatelessSessionInstanceInterceptor</interceptor> <!-- BMT --> <interceptor transaction="Bean">org.jboss.ejb.plugins.StatelessSessionInstanceInterceptor</interceptor> <interceptor transaction="Bean">org.jboss.ejb.plugins.TxInterceptorBMT</interceptor> <interceptor transaction="Bean" metricsEnabled="true">org.jboss.ejb.plugins.MetricsInterceptor</interceptor> <interceptor>org.jboss.resource.connectionmanager.CachedConnectionInterceptor</interceptor> </container-interceptors> <instance-pool>org.jboss.ejb.plugins.StatelessSessionInstancePool</instance-pool> <instance-cache></instance-cache> <persistence-manager></persistence-manager> <container-pool-conf> <MaximumSize>100</MaximumSize> </container-pool-conf> </container-configuration> </container-configurations> </jboss>
I used the standard Clustered Stateless SessionBean container as a template. You could also modify the default Clustered Stateless SessionBean container and just add the interceptor there.
Kudos to the JBoss developers for giving us the hooks necessary to come up with these kinds of solutions. -
4. Re: How to mark SLSB as unavailable?
anguyen Apr 6, 2005 3:53 PM (in response to anguyen)Oops, the catch block for Exception should look like this:
catch (Exception e) { Throwable rootCause = e; Throwable nextCause = null; while ((nextCause = rootCause.getCause()) != null) { rootCause = nextCause; } if (rootCause instanceof CreateException) { handleCreateException((CreateException) rootCause); } // if the root cause is not a CreateException, rethrow // the original exception throw e; }