views:

726

answers:

3

In my current project I'm dealing with EJBs implementing huge interfaces. Implementation is done through a business delegate, which implement the same interface and contains the real business code.

As suggested by some articles like

The usage sequence of this 'command pattern' is

  1. client creates a Command and parameterize it
  2. client send the Command to the server
  3. server receive command, log, audit and assert command can be served
  4. server execute command
  5. server return command result to client

The problem take place in step 4.:

Right now I'm using the spring context to get bean from context inside the command, but I want to inject dependencies into command.

Here is a naive usage for illustration purpose. I've added comments where I have problems:

public class SaladCommand implements Command<Salad> {    
    String request;

    public SaladBarCommand(String request) {this.request = request;}

    public Salad execute() {    
        //this server side service is hidden from client, and I want to inject it instead of retrieving it
        SaladBarService saladBarService = SpringServerContext.getBean("saladBarService");       
        Salad salad = saladBarService.prepareSalad(request);       
        return salad;
    }
}

public class SandwichCommand implements Command<Sandwich> {    
    String request;

    public SandwichCommand(String request) {this.request = request;}

    public Sandwich execute() {  
        //this server side service is hidden from client, and I want to inject it instead of retrieving it      
        SandwichService sandwichService = SpringServerContext.getBean("sandwichService");       
        Sandwich sandwich = sandwichService.prepareSandwich(request);       
        return sandwich;
    }
}

public class HungryClient {
    public static void main(String[] args) {
        RestaurantService restaurantService = SpringClientContext.getBean("restaurantService");
        Salad salad = restaurantService.execute(new SaladBarCommand(
            "chicken, tomato, cheese"
        ));
        eat(salad);

        Sandwich sandwich = restaurantService.execute(new SandwichCommand(
            "bacon, lettuce, tomato"
        ));
        eat(sandwich);
    }
}

public class RestaurantService {
    public <T> execute(Command<T> command) {
        return command.execute();
    }
}

I want to get rid of calls like SandwichService sandwichService = SpringServerContext.getBean("sandwichService"); and have my service injected instead.

How to do that the easiest way ?

A: 

If SimpleCommand is being injected into the classes using it by a Spring ApplicationContext (and really, it should be), then you simple need to express it's dependencies as constructor-arguments or setters and inject them as well.

It's hard to give any more specifics without understanding who is using SimpleCommand, where it comes from, etc.

matt b
SampleCommand is only a sample... it is an imaginary command. Commands are instantiated in a 'client' and are sent to a 'server'. So we can not use the Spring DI when creating command: dependencies are server side.
Guillaume
+1  A: 

I have built something amazingly similar in the past, except we did not use the command pattern like you are currently doing. In your case, your commands seem to do nothing but actually lookup and run a service method, so why not simply present that service method as the API instead of using the command pattern altogether. Then you can wire up the service calls to the EJB's via Spring Remoting, and all the Spring specifics can stay in the protocol specific layers (Servlet, EJB, MDB ...) and your code stays wonderfully ignorant of what is going on around it.

Our infrastructure looks like this. (For those that are going to complain about the existence of the EJB, this is not the entire infrastructure, and for security and performance reasons we use EJB to EJB calls for service to service interaction).

Eclipse Rich Client -> (Spring Remoting - HTTP) -> Servlet -> (Local Interface) -> EJB -> Service Implementation

The Servlet - Uses Spring context to look up the local EJB interface and calls the common invoke method of the generic EJB interface with the RemoteInvocation object (produced and sent by the HttpProxyFactoryBean from Spring Remoting) and the name of the service interface.

The EJB - Looks up the service based on its interface name (is also the bean name) and uses a RemoteInvocationExecutor to call a method on the service implementation with the RemoteInvocation object.

Now the EJB is capable of being tied to multiple services (although we use a one to one deployment model). You can use Spring Remoting for Http, EJB or JMS based calls to the service from different applications. Testing without the server deployment is trivial since you just wire up the tests directly to the implementations.

Note: I will try to add some code snippets if I get the chance.

Robin
Well command can perform more than only call a service method: Some can be more complex, and I can chain them. I want to replace a growing list of methods (sometime very similar) in our monster EJB with commands. Commands can be seen as 'remote callback'. I'm not wure to have a good understanding of "present that service method as the API" but to me it sound like the giant service interface I'm trying to get rid of...
Guillaume
I don't think this is a feasible model for distributed actions. You want to create a Command object on the client, with just data, but have the dependencies of that Command injected when it is reconstituted on the server side. To do this, you should probably construct a Command decorator on the server side with the client generated Command as a dependency, along with any other dependencies you have. Then your generic EJB will get the decorator via Spring (with services already injected) and inject the Command object as well before calling the execute method.
Robin
It still sounds like it would be simpler (and cleaner) to simplify your "big interface", break it up, make services that are compositions of others, that sort of thing. Then simply use those new interfaces to either hide, or replace the old one. This nicely separates your business data from your business services, which is what you want for remote services. Combining the data and actions in the same object doesn't lend itself to network service calls very well.
Robin
Thanks Robin. I know my question is a bit obscure and Your answers give me some hope. I think I'll go with the decorator. I can't break the 'big interface' for multiple reasons (political, time, ...) but with the command stuff maintaining it will be less painful.
Guillaume
A: 
public class SampleCommand implements Command {    
    private final String parameter;
    private final ServiceBean service;

    //the client build the command using a parameter
    public SampleCommand(ServiceBean service, String parameter) {
         this.parameter = parameter;
         this.service = service;
    }

    //this code will be executed by the server
    public Object execute() {
        //do something using the parameter and return the result
        return service.doSomethingWith(parameter);            
    }
}

You can inject a service with or without Spring:

<bean id="sampleCommand" class="package.SampleCommand">
     <constructor-arg ref="serviceBean" />
     <constructor-arg value="Param" />
</bean>

Some calling app:

ServiceBean service = ServiceProxy.getService(SampleCommand.class);
Command command = new SampleCommand(service, "Param");

Dependency Injection is not dependent on a framework.

Droo
It's not a problem of understanding how DI works with or without Spring or PicoContainer or whatever, it's about how to provide DI when a component is instantiated somewhere else in the network.
Guillaume
I think you may be over-engineering things a bit. Perhaps look at the Strategy pattern. A service should consume a command, not the other way around.
Droo