Announcement Announcement Module
Collapse
No announcement yet.
Context Passing with Spring/RMI Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Context Passing with Spring/RMI

    Hi all,
    I have recently picked up the Spring torch and am wondering about context passing from client to server. Here is my situation :

    I am using spring as framework on client and server in non-J2EE application. I am planning to use RMI to expose a number of services on the server side, using I guess RmiServiceExporter in Spring. The services need authorization but I don't want to pass a user name as a parameter as part of each operation on interface, so I need to transparently pass the user/security context to the server side (user name is all that is required). On the server, side I plan to use AOP to intercept the calls to these operations and delegate authorization to an aspect which connects to a legacy security service (which I must use) and checks if specified user name has permission for specified service. I don't think I need Acegi as all I want to propogate to server is the user name, the mapping of user name to roles is defined on server side in legacy security service.

    Can anyone point me in the right direction on how to accomplish this as I am a little worried about the following comment in the Spring reference docs :

    "Using Spring's support for RMI, you can transparently expose your services through the RMI infrastructure. After having this set up, you basically have a configuration similar to remote EJBs, except for the fact that there is no standard support for security context propagation or remote transaction propagation. Spring does provide hooks for such additional invocation context when using the RMI invoker, so you can for example plug in security frameworks or custom security credentials here."

    -- Is there any example of these "hooks" as I could not find any?

    Thanks in advance for any assistance
    /Tom

  • #2
    Hi Tom, I’m not aware of any example of that, but I did something similar some time ago. Not the same thing, but it propagates a “context” in the way you need. You can find the discussion and some code here. I don’t remember if this ties your RMI infrastructure to spring (it probably does) and if that will be a problem for you.

    Federico.

    Comment


    • #3
      Tom,
      Here's how I did this :
      Lingo (Spring Remoting) : Passing client credentials to the server
      http://www.jroller.com/page/sjivan?e...passing_client

      You may also be interested in this :
      Asynchronous calls and remote callbacks using Lingo Spring Remoting
      http://www.jroller.com/page/sjivan?e...allbacks_using

      Sanjiv

      Comment


      • #4
        Hi Guys,
        I'm trying to do this with the base spring functionality (i.e without Acegi or Lingo); if possible. What I have accomplished so far is that I have subclasses RemoteInvocation provided by spring and added an extra attribute to the invocation in the constructor, as indicated to do in the javadoc for RemoteInvocation .addAttribute();

        Code:
        public class CustomRemoteInvocation extends RemoteInvocation {
        	public CustomRemoteInvocation (MethodInvocation methodInvocation) {
        		super(methodInvocation);
        
        		// Invoked in superclass
        		this.addAttribute("userName", "test1111");
        	}
        }
        In clientBeans.xml, I then specify a custom invocation factory for the RMIProxyactoryBean.

        Code:
        <bean id="service" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
        <property name="remoteInvocationFactory" ref="invocationFactory"/>
        ....
        </bean>
        
        <bean id="invocationFactory" class="src.rmi.CustomRemoteInvocationFactory"/>
        This custom factory returns an instance of the CustomRemoteInvocation.

        In RemoteInvocationBasedAccessor.createRemoteInvocati on(..) I can see that it has my attribute is added to the RemoteInvocation.attributes map that is sent over as part of the invocation. My question is how can I intercept this CustomRemoteInvocation on the server side to read the attribute out again? It seems to me that I need to add an interceptor to the RmiServiceExporter but I dont know if thats possible.

        I hope that made any sense
        Thanks for reading
        /Tom

        Comment


        • #5
          Hi, You're doing fine, now you just need to inject an implementation of RemoteInvocationExecutor to the exporter to deal with the extra attributes on the server side.

          Federico.

          Comment


          • #6
            Originally posted by fschroder View Post
            Hi, You're doing fine, now you just need to inject an implementation of RemoteInvocationExecutor to the exporter to deal with the extra attributes on the server side.

            Federico.
            Wahey! Good work I now have it up and running:

            I created a custom executor which I then injected it into the service exporter like this:
            Code:
                  <bean id="customExecutor" class="rmipass.CustomExecutor"/>
            ...........
                  <property name="remoteInvocationExecutor" ref="customExecutor"/>
            I then wrote an aspect that declares a pointcut to intercept the "invoke" method on this custom executor:

            Code:
            @Pointcut ("execution(* CustomExecutor.invoke(RemoteInvocation, Object))")	
            public void inRemote() {}
            Finally I declare some advice to run before the invoke method is executed
            Code:
            @Before("inRemote() && args(invocation, ..)")
            public void validateAccount(RemoteInvocation invocation) throws NotAuthorizedException {
            	final String user = (String) invocation.getAttribute("userName");
            	System.out.println("User name from client :- " + user);
            
            	if (user == null || !users.contains(user)) {
            		System.err.println("Not authorized :- " + user);
            		throw new NotAuthorizedException(user);
            	}
            }
            This advice has access to the the user name and checks if it is in some set of defined users, as read from DB or somewhere. If can then just return if user is authorized and the invoke will run as normal. Otherwise an exception is thrown back to client.
            Using the @Before advice is following the "use the simplest thing that works idea"....could have used @Around but then I might forget to call proceed()!

            Thanks for your help guys,
            /Tom

            Comment


            • #7
              How about passing context from server to client?

              Hi guys,
              What do you suggest for passing some context info from RMI server to RMI client?
              We are trying to get RMI method execution timings back to the client that calls it, but in a transparent way: client should not know about the underlying passing of execution timings (we will log them for now, using Jmx bean or something to expose the timings is for later )

              Thank you.

              Madhav

              Comment


              • #8
                Hi,
                You could use AOP on the client side to measure the time taken to execute the method. In this case no timing info is exchanged over RMI.
                You could use a custom annotation as follows:

                Code:
                @Measure
                private void aMethod() {
                    rmiService.doSomething();
                }
                Then define an aspect that intercepts any call to a method wiht this annotation and starts a timer on it:

                Code:
                @Around("execution(@foo.Measure * *.*(..)) && @Measure(measure)") 
                public Object measureMethod(final ProceedingJoinPoint jp, final Measure measure) throws Throwable {
                    // Get start time
                
                    // Invoke actual method
                    final Object retVal = jp.proceed();
                
                    // Get end time
                
                    // Print the time taken
                
                    return retVal;
                }
                Obviously you could do something a little fancier, giving each invocation a name and storing its execution time in a map or something to print statistics later, but the main idea is there. The actual calling client code knows nothing about the measurement, nor does the server.

                Hope this helps
                /Tom
                Last edited by TOPPER_HARLEY; May 15th, 2007, 03:25 AM. Reason: Syntax error in code snippet

                Comment


                • #9
                  Thank you Topper.
                  Our intention is to measure three times: server side execution time, network latency and client side only time.
                  By measuring from client side alone, we can not determine all the three components above, hence the need for passing around context info from server to client.
                  There seem to be some third party solutions to this problem, but nothing simple.

                  Comment


                  • #10
                    Actually this is not that bigger deal.
                    You can measure at the client just before making the remote call (using the suggested aspect or by customizing the client object that sends the remoteinvocation to the server), then you must customize the objects that receives the remote invocation object on the server side to measure just after receiving, then invoke the method, and then measure again before sending the result back to the client. You can print/save this information on the server side or pack it along with the result and send it to the client. On the client side you measure again when the result arrives. At this point you should have all the information you need, you can calculate the network latency (approximately of course) doing some simple math with the results you got from the server and what's available on the client.
                    This approach is also transparent to the app, as the only classes that need to be modified are the ones that deal with the transmission and invocation.

                    Makes sense?
                    Federico.

                    Comment

                    Working...
                    X