Announcement Announcement Module
Collapse
No announcement yet.
Shared ApplicationContext betwee both web and ejb containers Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Shared ApplicationContext betwee both web and ejb containers

    Hello all,

    I have a common problem (it must be!), but I can't seem to find an actual soluton.

    My application consist of a webcontainer and a ejbcontainer within the same JVM. On startup, I create an ApplicationConext with the use of a ContextLoaderServlet.

    How do I allow the beans in the EJB container acces the same instance of the ApplicationContext!? Putting it in JNDI (as I first thought it should be done) doesn't seem to be an option since the ApplicationContext isn't serializable.

    I have looked at the BeanLocator interface, but I must be missing something. I can't figure the details out.

    I suspect that the classloader should be used as the share medium, but how?

    Creating one ApplicationContext per container isn't an option, since I also have Hibernate support within the context.

    This must be a common problem and solved thousands of times, but how? Any examples?

    All help is greatly appreciated!
    /Niklas

  • #2
    I posted a message on the developer mailing list that addresses the same problem. I didn't know about the BeanFactoryLocator then but I think this is the answer to our question. They only thing we need to do now is figure out how it works.

    My message posted on 11 jan:

    Consider an organization that has a number of applications each using
    Spring. Each application is deployed as a separate war in the same
    servlet container.

    An application can expose specific services which allow integration
    with other applications. Messaging is one solution for integration but
    if you can use global transactions direct method calls in the same
    thread is also a suitable integration pattern.

    I don't want to re-configure the service beans belonging to one
    application plus all its dependencies in each application that wants
    to call said application. Instead I want to do a call through a client
    library EJB style. Using Spring each application could provide the
    persistent classes, interfaces of exposed services and a Spring config
    file with a client setup to access these services. The service than
    would live in one place: the servlet context of the application it
    belongs to.

    But I don't want to use the remoting strategies currently included
    with Spring: HTTP invoker, Hessian, Burlap, RMI. For a number of
    reasons this is not suitable:

    - communication overhead in creating connections and serialization
    while in the same VM.
    - calls do not happen in the same thread -> no global transactions.

    So all I can think of is to do remoting through an intra-VM mechanism.
    I haven't got a clue on how to set up such a mechanism but I know it's
    a typical use case for EJB applications (local home interfaces).

    Comment


    • #3
      I found some references here:

      http://www.springframework.org/docs/reference/ejb.html
      Spring reference doc chapter 15: Accessing and implementing EJBs

      and here:

      http://www.devx.com/Java/Article/21665/0/page/3
      Article on devx: Spring: Creating Objects So You Don't Have To

      JndiBeanFactoryLocator and ContextJndiBeanFactoryLocator take the JNDI location of a context definition file. SingletonBeanFactoryLocator creates a singleton bean factory per class loader which could work for the web/EJB scenario but maybe not for the web/web scenario.

      Comment


      • #4
        Originally posted by tentacle
        JndiBeanFactoryLocator and ContextJndiBeanFactoryLocator take the JNDI location of a context definition file. SingletonBeanFactoryLocator creates a singleton bean factory per class loader which could work for the web/EJB scenario but maybe not for the web/web scenario.
        Thanks for your reply, but I still don't see the solution. The FactoryLocators creates new BeanFactoriy instances, one per class loader. This will result in 2 BeanFactories, one in the WebContainer's classloader and one in the EJBContainer's classloader.

        I somehow need to put my existing instance in a shared place, and be able to point to it from different containers/classloaders.

        Or can I somehow manually create the ApplicationContext in the top EAR classloader, which also should solve my problem?

        Comment


        • #5
          I posted a reply on my original message on the developer mailing list:

          Code:
          I've created a proof-of-concept implementation that allows intra-VM
          method invocations.
          I will probably release this code in a seperate jar but for now I
          would like your feedback. What's missing from is the proxy factory for
          clients and the exporter for the host.
          
          This interface needs to be implemented by the exporter:
          
          /**
           * This interface needs to be implemented by exporter classes that want to participate in intra-VM method
           * invocation in the same thread as the caller of the invoked method. This allows
           * for an EJB-style integration pattern by calling objects that live in the same VM but
           * are managed by a separate container. The typical use case would be a caller that lives in a
           * web application (WAR) and calls a service object that lives in the same VM but inside another
           * web application.
           * 
           */
          public interface InvocationHandler {
          
          	/**
          	 * The interface of the service that is called. This interface is shipped
          	 * by the application that hosts the service to other applications that want
          	 * to call the service as a client. The interface class functions a an end
          	 * point marked.
          	 * 
          	 * @return the service interface
          	 */
          	public Class getServiceInterface();
          	
          	/**
          	 * This method is called by an invocation mediator to pass method invocations.
          	 * 
          	 * @param invocation the method invocation
          	 * @return the result of the method invocation
          	 */
          	public Object handleInvocation(Object invocation);
          }
          
          
          This is the mediator that passes method invocations to
          InvocationHandler interfaces:
          
          /**
           * <p>This class offers a number of static methods to register and 
           * unregister InvocationHandler instances and pass method
           * invocations to these instance based on end point interfaces.
           * 
           * <p>In a servlet container setup where applications each living in a
           * separate web application &#40;WAR&#41; environment this class needs to be loaded
           * by the class loader that is shared by the web applications. The best location
           * to put the jar file would be in the lib directory of the servlet engine.
           * 
           */
          public class IntraVMInvocationMediator &#123;
          
          	private static Map invocationHandlers = new Hashtable&#40;&#41;;
          	
          	private IntraVMInvocationMediator&#40;&#41; &#123;
          		super&#40;&#41;;
          	&#125;
          
          	/**
          	 * Register a InvocationHandler instance with the mediator.
          	 * 
          	 * @param invocationHandler the InvocationHandler instance
          	 */
          	public static void register&#40;InvocationHandler invocationHandler&#41; &#123;
          		invocationHandlers.put&#40;invocationHandler.getServiceInterface&#40;&#41;, new WeakReference&#40;invocationHandler&#41;&#41;;
          	&#125;
          	
          	/**
          	 * Unregister a InvocationHandler instance with the mediator. 
          	 * 
          	 * @param invocationHandler the InvocationHandler instance
          	 */
          	public static void unregister&#40;InvocationHandler invocationHandler&#41; &#123;
          		invocationHandlers.remove&#40;invocationHandler.getServiceInterface&#40;&#41;&#41;;
          	&#125;
          	
          	/**
          	 * Pass a method invocation to a InvocationHandler instance based on the
          	 * name of the service interface end point.
          	 * 
          	 * @param serviceInterface the service interface end point
          	 * @param invocation the method invocation
          	 * @return the result of the method invocation
          	 */
          	public static Object invoke&#40;Class serviceInterface, Object invocation&#41; &#123;
          		Map tmpMap = new HashMap&#40;invocationHandlers&#41;;
          		
          		for &#40;Iterator iter = tmpMap.keySet&#40;&#41;.iterator&#40;&#41;; iter.hasNext&#40;&#41;;&#41; &#123;
          			Class tmpClass = &#40;Class&#41;iter.next&#40;&#41;;
          			if &#40;tmpClass.getName&#40;&#41;.equals&#40;serviceInterface.getName&#40;&#41;&#41;&#41; &#123;
          				WeakReference weakReference = &#40;WeakReference&#41;tmpMap.get&#40;tmpClass&#41;;
          				InvocationHandler invocationHandler = &#40;InvocationHandler&#41;weakReference.get&#40;&#41;;
          				if &#40;invocationHandler != null&#41; &#123;
          					return invocationHandler.handleInvocation&#40;invocation&#41;;
          				&#125; else &#123;
          					throw new RuntimeException&#40;"End point with interface &#91;" + serviceInterface.getName&#40;&#41; + "&#93; is not available!"&#41;;
          				&#125;
          			&#125;
          		&#125;
          		
          		throw new RuntimeException&#40;"End point with interface &#91;" + serviceInterface.getName&#40;&#41; + "&#93; is not available!"&#41;;
          	&#125;
          &#125;
          This is just a proof-of-concept. I'll create the missing classes. In a EJB server when your web and EJB applications are deployed in an EAR you would need to deploy the jar that contains these classes in APP-INF/lib.

          This approach lets you keep your instances wherever they live yet allows you to build a bridge between Spring bean factories through intra-VM remoting. You need to provide a jar file that contains the interfaces of the services you export plus the classes you use as parameters (if any) for the clients that call your services.

          Comment


          • #6
            Thanks for your intrest!

            However, how do you implement the proxies? As I understand your code, its actually the proxy that does the work that which I don't know how to do.

            Is it really this hard!? Must I be a superadvanced developer to solve this task?

            Any other, perhaps simpler idéas out there?

            /N

            Comment


            • #7
              Thanks for your intrest!
              Thanks for bringing it up.

              However, how do you implement the proxies? As I understand your code, its actually the proxy that does the work that which I don't know how to do.
              I just reuse Spring's existing remoting classes, extending them to create a service exporter and proxy factory bean. This is how the proxies are created.

              Any other, perhaps simpler idéas out there?
              No, there isn't. What we want to do is to execute code loaded in another application context. To achieve this we need to get hold of an instance. This happens through RemoteInvocation which is Spring's way to pass a method invocation back and forth across the wire.

              Normally there is a wire but in this case we want to remain in the same thread because we want to keep our transaction context, switching threads does not allow this. All in all this solution is pretty simple given that it achieves the same goal as local home interfaces achieve in EJB. I bet implementing that is a lot harder

              Comment

              Working...
              X