ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Designing a Fully Scalable Application
Pages: 1, 2

Scalable Stage and Dispatcher

MantaRay provides two types of mediators, point-to-point (stage) and publish-subscribe (dispatcher), which answer different mediation needs. Together, they form a complete set that serves all of the mediation needs described in this article.

Stage

The SEDA project defines a Staged Event-Driven Architecture that introduces a well decoupled methodology for module interaction where stages are used to moderate between modules. Modules queue events into stages, while other modules that serve as handlers to these stages receive the events, triggering an operation. These modules, in turn, queue events into other stages as needed.

Figure 6 shows the decoupling modules with a stage.

Figure 6
Figure 6. Decoupling modules with a stage

The MantaRay project takes the stage idea to the next level. Modules obtain a reference to stage through a factory object. The factory dynamically creates the proper implementation, depending on whether the stage is in a distributed environment or in memory.

Several modules (or several instances of the same module) can add themselves as handlers to the same stage, but a single event is sent to one and only one handler. An event can be any object that implements the marker interface Serializable. Most Java objects can be serialized, but objects like Socket and OutputStream can not cannot be serialized because by nature they cannot be passed through the network. You can add this marker interface to any object without needing to implement additional methods.

In the WebMonitor example, the GUI modules send a request to the engine to monitor a URL through a stage. The engines register themselves as handlers to the stage, which in turn acts as a software load balancer in addition to its role as a mediator.

Dispatcher

The dispatcher is very similar to a stage, with one significant difference. As opposed to a staged event that has one and only one handler, a dispatcher event is sent to all handlers that have added themselves to the dispatcher. Thus, a dispatcher serves as a one-to-many mediator.

Figure 7 shows the decoupling modules with a dispatcher.

Figure 7
Figure 7. Decoupling modules with a dispatcher

The same roles apply to the events dispatched on a dispatcher as to the events queued to a stage. Additionally, the methodology used to obtain a reference to the dispatcher through a factory is the same as the one used to obtain a stage.

In the WebMonitor example, engines notify the GUI about changes to the status of the monitored URLs through a dispatcher. All of the GUI modules register themselves as handlers to the dispatcher, and all of them get notifications about changes to the status of the monitored URLs.

Below is a short code example of how the GUI and the engine work with the stage and the dispatcher:

GUI Code

    import org.mr.api.blocks.ScalableDispatcher;
    import org.mr.api.blocks.ScalableFactory;
    import org.mr.api.blocks.ScalableHandler;
    import org.mr.api.blocks.ScalableStage;
    ...
    public class WebMonitorGui implements ActionListener
    , ScalableHandler{    
    // the input text of the url to be checked
    JTextField urlInput;
         
    // The outgoing stage to the engine
    public ScalableStage engineStage;
    
     // The inbound dispatcher from the engine
    public ScalableDispatcher guiDispatcher;
    
    /**
     * The GUI gets a reference to the incoming
     * dispatcher and the outgoing stage,
     * the distributed parameter is passed to the
     * factory of these objects
     * @param distributed. if this boolean is true then 
     * this sample is running in a distributed environment
     * and the engine is in a different VM
     */
    public WebMonitorGui(boolean distributed){
        // get the stage from the factory
        engineStage = ScalableFactory.getStage("engine"
            , distributed);
        // get the dispatcher from the factory
        guiDispatcher = ScalableFactory.getDispatcher(
            "gui", distributed);
        
        // register this object has handler to all
        // incoming events
        guiDispatcher.addHandler(this);
    }
    
    /**
     * Start the SWING GUI
     */
    public void startGUI(){
    //Make sure we have nice GUI.
        ..
    }//startGUI
    
    /**
     * Called when there is an input from the user
     * sends the URL input from the user to the engine
     * via the stage
     */
    public void actionPerformed(ActionEvent e) {
        engineStage.queue(urlInput.getText());
        
    }
    
    /**
     * This is an implementation method of 
     * ScalableHandler interface
     * Called by the dispatcher when an event (results)
     * is received from the engine, updates the display
     */
    public void handle(Object event) {
        HashMap result = (HashMap) event;
        Iterator urls = result.keySet().iterator();
        while(urls.hasNext()){
            String url = (String) urls.next();
            String status = (String) result.get(url);
            updateURLStatus(url,status);
        }    
    }
    
}//WebMonitorGui

Engine

    import org.mr.api.blocks.ScalableDispatcher;
    import org.mr.api.blocks.ScalableFactory;
    import org.mr.api.blocks.ScalableHandler;
    import org.mr.api.blocks.ScalableStage;
    ...
    
    public class WebMonitorEngine extends Thread
     implements ScalableHandler {
     
     //Inbound stage this engine gets its URL 
     //to check from this stage
    public ScalableStage engineStage;
    
     // Outbound dispatcher this engine returns a
     // result map on this dispatcher
    public ScalableDispatcher guiDispatcher;
    
     // the result map
    public HashMap urlsToStatus = new HashMap();
    
    /**
     * @param distributed if this boolean is true
     * then this sample is  running in a distributed
     * environment, the engine is in a different VM
     */
    public WebMonitorEngine(boolean distributed){
    
        //get the inbound stage and register as handler
        engineStage = ScalableFactory.getStage("engine"
            , distributed);
        engineStage.addHandler(this);
        // get the outbound dispatcher
        guiDispatcher = ScalableFactory.getDispatcher(
            "gui", distributed);
    }
    
    /**
     * This is an implementation method of
     * ScalableHandler interface. Called by the stage
     * when an event (request) was received from
     * the GUI, adds the URL to the "to checked" map
     * called urlsToStatus
     */
    public  void handle(Object event) {
            // The event is a string like "java.sun.com"
            String urlStr = (String) event;
            // put in map of URLs to be checked
            urlsToStatus.put(urlStr, "Checking");
        }
        
    }
    
    /**
     * The engine is a thread that runs and checks the
     * URL and creates a report about status changes
     */
    public void run(){
        while(true){
            HashMap result= getUrlNewStatus();
                // only is result is not empty then send it
                if(!result.isEmpty())
                    guiDispatcher.dispatch(urlsToStatus);
            }// while
        }// run
        
    }
}//WebMonitorEngine

The full code of the WebMonitor example using both stage and dispatcher methodologies can be found in the sample folder of MantaRay's latest release. This article simplified the code example to make it more suitable for the format of an article.

Conclusion

We all hope that we are successful, that our clients like the software we have developed, and that the software will be widely used, but we must make sure that we are ready for this success. We must plan ahead in order to ensure that when the time comes and millions of users are happily using our product, it can gracefully and flexibly scale up to handle any additional load, necessary modifications, and additional functionality.

MantaRay's scalable set of utilities makes it easy to create an application that is ready to be scaled up from day one, with no code modification. A factory method dynamically creates the required module-decoupling implementation, whether it is distributed or in memory, thus seamlessly transforming a small-scale application into an enterprise-grade one.

Resources

Amir Shevat is a senior software developer with eight years of experience in computing.


Return to ONJava.com.