Create Eclipse RAP Widget from ClientScripting-based widget

1- Introduction

The document was written based on:
  • Eclipse 4.4 (LUNA)

  • Eclipse RAP 2.3

2- ClientScripting-Based Widget

Suppose that you have a Widget written in JavaScript, you want to build a RAP Widget based on  above JavaScript widget. It is quite feasible.

Examples LogJS is a widget written in javascript, it includes 2 files logjs.js & logjs.css . Our target is to create a widget on RAP use LogJS.
Images of LogJS on html pages.
logjs.js

function LogJS(element)  {
   
   this.div = document.createElement("div");    
   this.div.className = 'logjs';
   
   element.appendChild(this.div);
   
   this.div.innerHTML = "<p>INFO: Loaded APR based Apache Tomcat Native library 1.1.22.</p>";
   
   
   this.appendInfo = function(text)  {        
       this.div.innerHTML = this.div.innerHTML
        + "<p class='info'>INFO: "+ text +"</p>";
   };
   
   this.appendErr = function(text)  {  
       this.div.innerHTML = this.div.innerHTML
        + "<p class='err'>ERROR: "+ text +"</p>";
   };
   
   this.appendWarn = function(text)  {        
       this.div.innerHTML = this.div.innerHTML
        + "<p class='warn'>WARN: "+ text +"</p>";
   };
               
   this.clearAll = function()  {  
       this.div.innerHTML = "";
   };
}
logjs.css
.logjs {
 width: 100%;
 height: 100%;
 margin: 0px;
 padding: 5px;
 border: 1px solid #C4C4C4;
 color: #0AC005;
 background-color: #111111;
 font: 13px Verdana, "Lucida Sans", Arial, Helvetica, sans-serif;
 white-space: nowrap;
 overflow-y: scroll;
 overflow-x: scroll;
}

.logjs p {
   margin: 0px;
   padding: 0px;
}

.logjs .info {
 
}

.logjs .err {
 color: red;
}

.logjs .warn {
 color: yellow;
}
index.html
<html>

<head>
 <script type="text/javascript" src="logjs.js"></script>
 <link rel="stylesheet" type="text/css" href="logjs.css" />
</head>
<body>


   <div id="logjs1" style="width: 500px; height: 300px;"></div>
   <br><br>
   <button onclick="appendInfo('Starting Servlet Engine: Apache Tomcat/7.0.23')">Add Info Line</button>
   <button onclick="appendErr('Starting Servlet Engine: Apache Tomcat/7.0.23')">Add Error Line</button>
   <button onclick="appendWarn('Starting Servlet Engine: Apache Tomcat/7.0.23')">Add Warn Line</button>  
 
 
  <script type="text/javascript">
     var element = document.getElementById('logjs1');
     
     var logjs = new LogJS(element);  
     
     function appendErr(text)  {  
        logjs.appendErr(text);      
     }  
     function appendWarn(text)  {
        logjs.appendWarn(text);      
     }  
     
     function appendInfo(text)  {
        logjs.appendInfo(text);      
     }  
  </script>


</body>
</html>

3- RAPTarget

First we need to create a project to configure the RAP environment .Essentially, it is declarations of the RAP library.
Create a Java project - RAPTarget.
Create file "Target Define"
Add library
Khai báo thư viện RAP
  • Name: RAP Runtime 2.3
  • Location: http://download.eclipse.org/rt/rap/2.3
Click "Set as Target Platform" for the the  RAP library work with every Project in the workspace.

4- Create Plugin Project - LogComposite

  • File/New/Other...
Enter:
  • Project Name: LogComposite
Do not select any Template.
Your project has been created:
Declare plugins required for this Plugin:
  • org.eclipse.rap.ui
  • org.eclipse.equinox.http.registry
You need to create a logjsreources package and copy the javascript  files, related style in this package. As shown below below:
Next create 2 javascript file:
  • rap-handler.js
    •   Acting as the intermediary communication between RAP Widget  and ClientScripting-based Widget 
  • load-css-file.js 
    • this file is in charge of load css files to web pages (specifically here is logjs.css).
load-css-file.js
//
var cssId = 'logjs-css-file';  


if (!document.getElementById(cssId))
{
   var head  = document.getElementsByTagName('head')[0];
   var link  = document.createElement('link');
   link.id   = cssId;
   link.rel  = 'stylesheet';
   link.type = 'text/css';
   link.href = '/rwt-resources/logjs/logjs.css';
   link.media = 'all';
   head.appendChild(link);
}
rap-handler.js
(function() {
   'use strict';

   rap.registerTypeHandler("o7planning.LogComposite", {

       factory : function(properties) {
           return new o7planning.LogComposite(properties);
       },

       destructor : "destroy",

       properties : [ "abc" ],

       events : [],

       methods : [ 'appendWarn', 'appendErr', 'appendInfo', 'clearAll' ]

   });

   if (!window.o7planning) {
       window.o7planning = {};
   }


   // Constructor
   o7planning.LogComposite = function(properties) {

       bindAll(this, [ "layout", "onReady", "onSend", "onRender", "onChange" ]);// @custom
       this.parent = rap.getObject(properties.parent);
       this.element = document.createElement("div");
       this.parent.append(this.element);
       this.parent.addListener("Resize", this.layout);

       this.logjs = new LogJS(this.element);
       
   
       // Render interface
       rap.on("render", this.onRender);
   };

   o7planning.LogComposite.prototype = {

       ready : false,

       onChange : function() {
           
       },

       onReady : function() {

       },

     
       // Render interface in Client
       onRender : function() {
           if (this.element.parentNode) {
               rap.off("render", this.onRender);
               
               rap.on("render", this.onRender);
               rap.on("send", this.onSend);
           }
       },

       //  
       onSend : function() {

       },

       destroy : function() {
           rap.off("send", this.onSend);
           try {
               this.element.parentNode.removeChild(this.element);
           } catch (e) {
               try {
                   console
                           .log('error call this.element.parentNode.removeChild(this.element) :'
                                   + e);
               } catch (e) {
               }
           }
       },

       layout : function() {
           if (this.ready) {
               var area = this.parent.getClientArea();
               this.element.style.left = area[0] + "px";
               this.element.style.top = area[1] + "px";
               this.editor.resize(area[2], area[3]);
           }
       },

       setAbc : function(abc) {
       },

       appendErr : function(json) {  
           var text= json["text"];
           this.logjs.appendErr(text);
       },

       appendWarn : function(json) {  
           var text= json["text"];
           this.logjs.appendWarn(text);
       },
       
       appendInfo : function(json) {  
           var text= json["text"];
           this.logjs.appendInfo(text);
       },
       
       clearAll : function()  {  
           this.logjs.clearAll();
       }
   };

   var bind = function(context, method) {
       return function() {
           return method.apply(context, arguments);
       };
   };

   var bindAll = function(context, methodNames) {
       for (var i = 0; i < methodNames.length; i++) {
           var method = context[methodNames[i]];
           context[methodNames[i]] = bind(context, method);
       }
   };

   var async = function(context, func) {
       window.setTimeout(function() {
           func.apply(context);
       }, 0);
   };

}());
LogComposite is a Widget extends from Composite class and have some method like LogJS
  • appendErr
  • appendInfo
  • appendWarn
  • clearAll
LogComposite.java
package org.o7planning.customwidget.logcomposite;

import java.io.IOException;
import java.io.InputStream;

import org.eclipse.rap.json.JsonObject;
import org.eclipse.rap.json.JsonValue;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.JavaScriptLoader;
import org.eclipse.rap.rwt.remote.AbstractOperationHandler;
import org.eclipse.rap.rwt.remote.Connection;
import org.eclipse.rap.rwt.remote.OperationHandler;
import org.eclipse.rap.rwt.remote.RemoteObject;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.rap.rwt.widgets.WidgetUtil;
import org.eclipse.swt.widgets.Composite;

public class LogComposite extends Composite {


   private static final long serialVersionUID = -8590973451146216709L;

   private RemoteObject remoteObject;


   // The directory containing the file js, css.
   private static final String REAL_RESOURCE_PATH = "logjsresources";

   private static final String REGISTER_PATH = "logjs";

   private static final String REMOTE_TYPE = "o7planning.LogComposite";

   private final String[] FILENAMES = { "logjs.css", "logjs.js" ,"load-css-file.js" , "rap-handler.js" };

   private final OperationHandler operationHandler = new AbstractOperationHandler() {

       private static final long serialVersionUID = -1979566336567602883L;

       @Override
       public void handleSet(JsonObject properties) {
           System.out.println("##### handleSet ..:");
           JsonValue textValue = properties.get("text");
           if (textValue != null) {
               // text = textValue.asString();
           }
       }

       @Override
       public void handleCall(String method, JsonObject parameters) {
           System.out.println("##### handleCall ..:" + method);
       }

       @Override
       public void handleNotify(String event, JsonObject properties) {
           System.out.println("##### handleNotify ..:" + event);
           if (event.equals("dirty")) {
           }
       }

   };

   /**
    * Create the composite.
    *
    * @param parent
    * @param style
    */
   public LogComposite(Composite parent, int style) {
       super(parent, style);

       // Note: Catching error when viewed on WindowBuilder
       try {
           registerResources();
           loadJavaScript();

           Connection connection = RWT.getUISession().getConnection();
           remoteObject = connection.createRemoteObject(REMOTE_TYPE);
           remoteObject.setHandler(operationHandler);

           //
           remoteObject.set("parent", WidgetUtil.getId(this));

       } catch (Exception e) {
           e.printStackTrace();
           // throw new RuntimeException(e);
       }
   }
   

   @Override
   public void dispose()  {
       super.dispose();        

       // Call destroy() function in rap-handler.js
       remoteObject.destroy();
   }


   // Load the js files required at Client.
   private void loadJavaScript() {
       JavaScriptLoader jsLoader = RWT.getClient().getService(
               JavaScriptLoader.class);
       ResourceManager resourceManager = RWT.getResourceManager();

       // Load file logjs.js into page
 
       jsLoader.require(resourceManager.getLocation(REGISTER_PATH + "/"
               + "logjs.js"));

       // Load file load-css-file.js into page
   
       jsLoader.require(resourceManager.getLocation(REGISTER_PATH + "/"
               + "load-css-file.js"));
       
       // Load file rap-handler.js into page.

        jsLoader.require(resourceManager.getLocation(REGISTER_PATH + "/"
                  + "rap-handler.js"));
   }

   private void registerResources() throws IOException {
       ResourceManager resourceManager = RWT.getResourceManager();

       for (String fileName : FILENAMES) {

           // After registering, you can access on your browser:
           
           // (http://localhost:port/rwt-resources/logjs/abc.js )
           // logjs/abc.js            
           String path = REGISTER_PATH + "/" + fileName;

            // Check this resource has been registered yet.
           boolean isRegistered = resourceManager.isRegistered(path);

           if (!isRegistered) {
               ClassLoader classLoader = LogComposite.class.getClassLoader();
               // Real Path (in src)
               
               // logjsresources/abc.js
               String realPath = REAL_RESOURCE_PATH + "/" + fileName;

               InputStream inputStream = classLoader
                       .getResourceAsStream(realPath);
               if (inputStream == null) {
                   throw new IOException("File not found " + realPath);
               }
               try {
                   // Register resource                    
                   resourceManager.register(path, inputStream);
               } finally {
                   inputStream.close();
               }
           }
       }
   }

   @Override
   protected void checkSubclass() {
   }

   public void appendWarn(String text) {
       System.out.println("appendWarn");
       JsonObject obj= new JsonObject();
       obj.add("text", text);
       this.remoteObject.call("appendWarn", obj);
   }
   
   public void appendErr(String text) {
       System.out.println("appendErr");
       JsonObject obj= new JsonObject();
       obj.add("text", text);
       this.remoteObject.call("appendErr", obj);
   }
   
   public void appendInfo(String text) {
       System.out.println("appendInfo");
       JsonObject obj= new JsonObject();
       obj.add("text", text);
       this.remoteObject.call("appendInfo", obj);
   }
   
   public void clearAll() {
       System.out.println("clearAll");
       this.remoteObject.call("clearAll", new JsonObject());
   }
}
You need to export the package org.o7planning.customwidget.logcomposite, to allow other plugins can use LogComposite class in this plugin.

5- Test LogComposite

You need to create a Project to test LogComposite. Here, I create a basic RAP application.
  • File/New/Other...
Registered to use  LogComposite plugin
Open BasicEntryPoint class, modified to allow you to design it on WindowBuilder.
    @Override
    protected void createContents(Composite parent) {
        
        // Change code, to enable open with WindowBuilder.
        parent.setLayout(new FillLayout());
        Composite main = new Composite(parent, SWT.NONE);
    }
Nhấn phải chuột vào class BasicEntryPoint và chọn "Open with/WindowBuilder Editor".
LogComposite is available on Palette, like the other Widget, you can easily drag and drop it into your design interface.
BasicEntryPoint.java
package simplerapapplication;

import org.eclipse.rap.rwt.application.AbstractEntryPoint;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.o7planning.customwidget.logcomposite.LogComposite;


public class BasicEntryPoint extends AbstractEntryPoint {
   private LogComposite logComposite;

   /**
    * @wbp.parser.entryPoint
    */
   @Override
   protected void createContents(Composite parent) {
       
       parent.setLayout(new FillLayout());
       Composite main = new Composite(parent, SWT.NONE);
       main.setLayout(new GridLayout(1, false));
       
       logComposite = new LogComposite(main, SWT.BORDER);
       logComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
       
       Composite composite = new Composite(main, SWT.NONE);
       composite.setLayout(new RowLayout(SWT.HORIZONTAL));
       composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
       
       Button btnNewButton = new Button(composite, SWT.NONE);
       btnNewButton.addSelectionListener(new SelectionAdapter() {
           @Override
           public void widgetSelected(SelectionEvent e) {
               allErrorLine();
           }
       });
       btnNewButton.setText("Add Error Line");
       
       Button btnNewButton_1 = new Button(composite, SWT.NONE);
       btnNewButton_1.addSelectionListener(new SelectionAdapter() {
           @Override
           public void widgetSelected(SelectionEvent e) {
               addWarningLine();
           }
       });
       btnNewButton_1.setText("Add Warning Line");
       
       Button btnNewButton_2 = new Button(composite, SWT.NONE);
       btnNewButton_2.addSelectionListener(new SelectionAdapter() {
           @Override
           public void widgetSelected(SelectionEvent e) {
               clearAll();
           }
       });
       btnNewButton_2.setText("Clear ALL");
   }
   
   
   private void allErrorLine()  {
       logComposite.appendErr("Starting Servlet Engine: Apache Tomcat/7.0.23");
   }
   
   
   private void addWarningLine()  {
       logComposite.appendWarn("Starting Servlet Engine: Apache Tomcat/7.0.23");
   }
   
   private void clearAll()  {
       logComposite.clearAll();
   }

}

Running application:

Right-click the "SimpleRAPApplication" project, select Run As/RAP Application
Above error shows that you need RAP runtime configure, before running the application.
The results run on RAP: