Class pollution and the EJB spec

Joseph Marques jmarques at redhat.com
Sat May 8 23:53:14 UTC 2010


Remember, the Remote API wasn't introduced until RHQ 1.3.0.  Thus, all 
people ever did was code their *Locals, and then implement them in the 
*ManagerBeans.

However, I suppose because we never exposed any of our methods, and 
because we never really had up-front standards for our *Locals, the 
style and form and names of these methods were grossly inconsistent 
across SLSBs (and even sometimes in the same SLSB with different 
authors).  If we had opened these methods up to remote clients, 
customers would have been pulling their hair out trying to use our 
remote API.

And so, even though we were strapped for time, we knew we had to fix 
these API issues before our first release (otherwise, even if we fixed 
them in the future, we'd have to support both the crappy API as well as 
the refined API).  Accordingly, 40 or so rules and conventions were 
adopted to help close the inconsistency gap, which are documented here:

http://www.rhq-project.org/display/RHQ/Design-Remote+API#Design-RemoteAPI-ApproachandConventions

But that was only the first of two major problems with the remote API.  
It turns out that our implementations of the JBoss Remoting invocation 
handlers were performing double serialization.  EJB3 would serialize 
results once when the invocation handler's invoke() method would call 
across the *Remote boundary of the target SLSB, and JBoss Remoting would 
serialize the results again as they were being returned by invoke() 
method itself.  On top of that, we also wanted to avoid remote clients 
getting the classic Hibernate LazyInitializationException when trying to 
access data that hasn't been fetched.  Thus, we wanted to "cleanse" all 
of our data by passing the results from the SLSBs through our 
HibernateDetachUtility, which unwraps all of the Hibernate proxies and 
replaces them with their actual raw entity counterparts from core/domain.

So the solution was to avoid calling across the remote interfaces 
altogether.  Our service invocation handlers would instead call across 
the locals, which would hand us back object references (as opposed to 
invoking serialization).  We then take these objects and before 
returning them out of the invoke() method, run them through 
HibernateDetachUtility.  As a consequence of doing this (calling across 
the *Local boundary), we had to ensure that clients (CLI and WS) weren't 
calling methods that they shouldn't be allowed to (i.e. getOverlord()).  
The solution was to only allow method invocations on the *Local if that 
same method was declared on the *Remote interface.  To do this we would 
first lookup the request to see if the target method signature exists on 
the *Remote interface, and if it did we look up the same method on the 
*Local interface and call that one instead.  The invoke method can thus 
be distilled as:

Object invoke(InvocationRequest request) {
      NamedBasedInvocation nbi = // get from request
      String methodName = // get from 'nbi'
      Object result = null;
      try {
           // Lookup the remote first, if it doesn't exist exit with error.
           // This prevents remote clients from accessing the locals.
           ic.lookup("rhq/" + methodName + "/remote");

           Object target = ic.lookup("rhq/" + methodName + "/local");

           // do actual invocation
      } ...exception handling...
      } finally {
           result = useHibernateDetachUtility(result);
      }
      return result;
}

The net effect of this was that all of our *Local interfaces had to 
define ALL of the same methods that we exposed in the *Remote 
interface.  In other words, the Locals were always a superset of the 
Remotes.  Given this, it was possible to write the Local interfaces by 
extending their Remote interface counterparts.

And so it was this late realization of the double serialization that 
caused most of the original remote/locals pairs to be written without 
using extension...because initially one wasn't necessarily going to be a 
subset of the other (but now it's required).

The primary reason for even using the "Local extends Remote" pattern was 
to eliminate inconsistencies that might arise if people added a remote 
method but then didn't have that same method defined on the local 
interface.  Granted, the probably of this slipping by us should tend 
towards 0 if we have good testing practices in place, such that we 
always create unit test methods for everything.  But let's be honest 
with ourselves - our unit tests are no where near full coverage today 
(that should change!)  Also, keep in mind that time tables were 
extremely tight for getting all of this done for before RHQ 1.3.0 went 
GA, and so getting full-coverage testing (for better or worse) was not a 
blocker for release.  In short, we did the best that we could, given 
little time to actually do all of this, needing to make **thousands** of 
lines of refactoring changes according to the new code conventions, and 
then making sure that the locals were a superset of the remote interface 
due to the serialization issue workaround.

In any event...that release is over and done with, and we have a little 
more time now to breathe and figure out what the right solution for us 
is.  Do we want to use the "Local extends Remote" pattern across the 
board?  Or do we want to revert things back such that all remote methods 
need to be duplicated in the local interfaces?  I could go either way.

On 05/07/2010 12:24 PM, Jay Shaughnessy wrote:
> We actually used to do it the way you're suggesting until someone, can't
> remember who, suggested that we pull the remote decls out of the local
> in favor of the "implements remote" approach.  No one could remember why
> we had done it the original way but perhaps it was for JDK5 compatibility.
>
> Of course the new approach looks much cleaner and is easier to deal
> with.  If there is any we can keep it this way it would be nice.
>
> On 5/7/2010 7:22 AM, Heiko W.Rupp wrote:
>    
>> Hi,
>>
>> I see the following the following pattern being used more and more in our code
>>
>> @Stateless
>> public class MyBean implements MyBeanLocal {
>> ...
>> }
>>
>>
>> @Local
>> public interface MyBeanLocal implements MyBeanRemote
>> {
>>      public void foo();
>> ...
>> }
>>
>> @Remote
>> @Webservice
>> @SomethingElse
>> public interface MyBeanRemote
>> {
>>       @WebMethod public void foo();
>> ...
>> }
>>
>> I am currently seeing the following  issues with that: on jdk15, @Webservice, @SomethingElse need to be on the classpath of clients using MyBeanLocal for compilation.
>> Actually (but I have not found an example forbidding it), I am not sure if the pattern above is portable, as the spec does not talk about
>> the semantics of such inheritance of @WebService and @WebMethod (as opoosed to the inheritance of @TransactionAttribute)
>>
>> So I think it would be better in the future not to use this above pattern - the IDE actually allows to easily pull up a method form
>> a session bean class into both interfaces if needed.
>>     Heiko
>>
>>
>>
>>      
> _______________________________________________
> rhq-devel mailing list
> rhq-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/rhq-devel
>    



More information about the rhq-devel mailing list