DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • JQueue: A Library to Implement the Outbox Pattern
  • The Foundations for Building an Apache Flink Application
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • Clean Up Event Data in Ansible Event-Driven Automation

Trending

  • MySQL to PostgreSQL Database Migration: A Practical Case Study
  • DGS GraphQL and Spring Boot
  • Building Resilient Networks: Limiting the Risk and Scope of Cyber Attacks
  • AI-Driven Test Automation Techniques for Multimodal Systems
  1. DZone
  2. Data Engineering
  3. Data
  4. Eclipse's RAP Push Session Revisited

Eclipse's RAP Push Session Revisited

If you're writing code for a web application using Java, read this post to learn about how to work with the newest updates to Eclipse.

By 
Kees Pieters user avatar
Kees Pieters
·
Updated Aug. 09, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
6.1K Views

Join the DZone community and get the full member experience.

Join For Free

A few years ago, I wrote an article on Eclipse's RAP Push Session mechanism. To review, the Remote Application Platform team faced the problem that a full-fledged rich client application comes with its conundrums when this needs to be implemented with HTTP and JavaScript. A regular rich client can respond to any event that it receives, while HTTP always starts with an event that is spawned in the client itself. Usually, this problem is tackled by polling the event source, and generating a UI event when the event source changes. The Push Session mechanism provided such a mechanism in Eclipse RAP but needed a bit of boilerplate code to make it work. My previous post addressed this issue.

I've been using the proposed mechanism for a few years, without too much ado, but there were a few developments that made me reconsider the solution I wrote about. The main ones are:

  1. The solution seemed less efficient for extremely fast events, which caused the rich client to freeze every now and then.
  2. I realized that the Push Session is always activated by event listeners, which allowed for a more efficient design.
  3. The proposed solution sometimes makes the handling of Java to JavaScript calls problematic.

The latter issue may need some further explanation. One of the libraries I have developed is an OpenLayers plugin, which is used to create and handle maps. The calls are made in Java, which are transformed into JavaScript functions that are consequently handled by OpenLayers. A callback function notifies listeners about the success of these calls and is sometimes used to generate new calls to the OpenLayers map.

The nature of this approach in RAP is such that a call has to be completely handled before the next call can be made. If not, a  "JavaScript function is already pending" exception is thrown, and the next call will not be processed. I worked around this problem by first collecting all the JavaScript calls that are required as a response to an event, prior to calling a synchronize()  function that processes all the calls as a batch.

With the push session mechanism, this approach becomes problematic! An event may notify a number of listeners about a change, and all these may affect the OpenLayers map. As the handling of the event in the UI has to be performed in the UI thread, the result was that all the listeners were processed simultaneously, and the calls to OpenLayers were interfering with each other. As a result, the improved Push Session mechanism I present here will give a notification when all the listeners have been processed, after which the synchronise() call can be made.

An Improved Push Session Handler

As mentioned earlier, the basic principles of the previous post are still correct, so I will not dwell too much on the implementation details. The basic premise of the session handler is that a listener can provide a data object (usually a  java.util.EventObject ), after which a Display.asynexec() call will handle this at some point. This is a fairly standard approach for UI handling. The Session handler adds the Push Session mechanism and sees to it that a new Push Session is started when the handling of the UI has been completed.

The improved session handler can collect multiple events, prior to the processing of these events by Display.asynexec(), and sees to it that this call only has to be made once, which greatly improves the UI performance. The resulting code is depicted below:

Java
 
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.rap.rwt.service.ServerPushSession;
import org.eclipse.swt.widgets.Display;

public class RefreshSession {

    private Display display;
    private ServerPushSession session;
    private Collection data;
    private boolean started;
    private boolean refresh;
    private Collection> listeners;

    public RefreshSession() {
        listeners = new ArrayList>();
        this.started = false;
        this.refresh = false;
        data = new ArrayList<>();
        session = new ServerPushSession();
    }

    public void addSessionListener( ISessionListener listener ){
        this.listeners.add( listener );
    }

    public void removeSessionListener( ISessionListener listener ){
        this.listeners.remove( listener );
    }

    public void init( Display display ){
        this.display = display;
    }

    /**
     * Called to refresh the UI
     */
    public synchronized void addData( T data ){
        this.data.add( data );
        refresh();
    }

    public void start(){
        session.start();
        this.started = true;
    }

    public void stop(){
        this.started = false;
        session.stop();
    }

    protected void notifyListeners( SessionEvent event ) {
        for( ISessionListener listener: listeners){
            try{
                if( listener != null )
                    listener.notifySessionChanged( event );
            } catch( Exception ex ){
               ex.printStackTrace();
            }
        }
    }

    public void dispose(){
        this.listeners.clear();
        this.display = null;
        this.stop();
    }

    protected void refresh() {
        if( !started || this.refresh || ( display == null ) || ( display.isDisposed()))
            return;
        this.refresh = true;
        display.asyncExec(
            new Runnable() {

                @Override 
                public void run() {
                   try{
                        SessionEvent event = null;
                        data.clear();
                        for( T dt: data ) {
                            event = new SessionEvent( this, dt );
                            notifyListeners(event);
                        }
                        refresh = false;
                        session.stop();
                        notifyListeners( new SessionEvent( this, ISessionListener.EventTypes.COMPLETED, null ));
                    }
                    catch( Exception ex ){
                        ex.printStackTrace();
                    }
                    start();
                }
            });
        }
    }
}


This bit of code is fairly similar to my previous post but is better optimized to handle incoming events. All that remains to be done is to:

  • Add the RefreshSession to a widget, for instance, a org.eclipse.swt.Composite class.
  • Call the addData() method in the event handlers.
  • Add a session listener to handle the code.
  • Dispose of the RefreshSession when the widget is disposed.

Obviously, this bit of boilerplate code can also be abstracted, resulting in the AbstractSessionHandler depicted below:

import org.eclipse.swt.widgets.Display;
/**
 * Handle the session. This is usually implmenented as an inner class in
 * a composite or other widget
 * @author keesp
 *
 * @param
 */
 public abstract class AbstractSessionHandler {
    private RefreshSession session;
    private ISessionListener listener = new ISessionListener(){
        @Override public void notifySessionChanged(SessionEvent event) {
            onHandleSession( event );
        }
    };

    public void addData( D data ) {
        session.addData(data);
    }

    protected AbstractSessionHandler( Display display ) {
        this.session = new RefreshSession<>();
        this.session.init( display );
        this.session.addSessionListener( listener);
        this.session.start();
    }

    protected abstract void onHandleSession( SessionEvent sevent );
        
    public void dispose() {
        this.session.removeSessionListener(listener);
        this.session.stop();
    }
}


You can now easily add this handler as an inner class in your widget, as follows:

import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import java.util.EventObject;
import org.eclipse.swt.SWT;

....

public class MyComposite extends Composite {
    private static final long serialVersionUID = 1L;

   ....

    private SessionHandler handler;

    public MyComposite(Composite parent, int style) {
        super(parent, style);
        ....
        handler = new SessionHandler( super.getDisplay());
    }

    @Override
    public void dispose() {
        this.handler.dispose();
        ....
        super.dispose();
    }

    private class SessionHandler extends AbstractSessionHandler<MyEvent> implements IMyListener{
        protected SessionHandler(Display display) {
            super(display);
        }

        @Override protected void onHandleSession(SessionEvent<MyEvent> sevent) {
            try{
                /** HANDLE YOUR EVENT HERE **/
            }
            catch( Exception ex ){
                ex.printStackTrace();
            }
        }

        /**
         * This bit of code implements the event listener and is always the same!
        */
        @Override
        public void notifyChanged(MyEvent event) {
            if( getDisplay().isDisposed() )
                return;
            super.addData(event);
        }
    }
}


Conclusion

With the above code, much of the boilerplate code required to implement a push session in Eclipse RAP has been abstracted away and results in efficient handling of external events by the graphical user interface.  

Boilerplate code Eclipse Data (computing) Event push Session (web analytics)

Opinions expressed by DZone contributors are their own.

Related

  • JQueue: A Library to Implement the Outbox Pattern
  • The Foundations for Building an Apache Flink Application
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • Clean Up Event Data in Ansible Event-Driven Automation

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!