Tạo Eclipse RAP Widget từ ClientScripting widget
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

1- Giới thiệu

Tài liệu được viết dựa trên:
  • Eclipse 4.4 (LUNA)

  • Eclipse RAP 2.3

2- ClientScripting-Based Widget

Giả sử bạn có một Widget viết bằng Javascript, bạn muốn xây dựng một RAP Widget dựa javasript widget nói trên. Việc đó hoàn toàn khả thi.

Ví dụ LogJS là một widget viết bằng javascript, nó bao gồm 2 file logjs.js & logjs.css. Mục tiêu của chúng ta là tạo ra một widget trên RAP sử dụng LogJS.
Hình ảnh của LogJS trên trang html.
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

Trước hết chúng ta cần tạo một project để cấu hình môi trường RAP. Về bản chất là khai báo các thư viện RAP.
Tạo một Java project - RAPTarget.
Tạo file "Target Define"
Thêm vào các thư viện
Khai báo thư viện RAP
  • Name: RAP Runtime 2.3
  • Location: http://download.eclipse.org/rt/rap/2.3
Nhấn nút "Set as Target Platform" để cho thư viện RAP có tác dụng với mọi Project trong workspace.

4- Tạo Plugin Project - LogComposite

  • File/New/Other...
Nhập vào:
  • Project Name: LogComposite
Không chọn một Template nào.
Project của bạn đã được tạo ra:
Khai báo các plugin đòi hỏi cho Plugin này:
  • org.eclipse.rap.ui
  • org.eclipse.equinox.http.registry
Bạn cần tạo một package logjsreources và copy các file javascript, style liên quan vào trong package này. Như hình minh họa dưới đây:
Tiếp theo tạo 2 file javascript:
  • rap-handler.js
    • Làm nhiệm vụ giao tiếp trung gian giữa RAP WidgetClientScripting-based Widget.
  • load-css-file.js
    • File này làm nhiệm vụ tải các file css lên trang web (Cụ thể ở đây là 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 = {};
   }

   // Hàm khởi tạo đối tượng tại Client.
   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);
       
       // Vẽ giao diện.
       rap.on("render", this.onRender);
   };

   o7planning.LogComposite.prototype = {

       ready : false,

       onChange : function() {// @custom
           
       },

       onReady : function() {

       },

       // Vẽ giao diện tại 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 là một Widget mở rộng từ class Composite và có một số method giống như 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;

   // Thư mục chứa các file js,css thư viện.
   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);

       // Chu y: Bat loi khi view tren 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);
       }
   }
   
   // Hủy đối tượng
   @Override
   public void dispose()  {
       super.dispose();        
       // Gọi vào hàm destroy() trong rap-handler.js.
       remoteObject.destroy();
   }

   // Tải các file js cần thiết tại Client.
   private void loadJavaScript() {
       JavaScriptLoader jsLoader = RWT.getClient().getService(
               JavaScriptLoader.class);
       ResourceManager resourceManager = RWT.getResourceManager();

       // Load file logjs.js vào trang        
       jsLoader.require(resourceManager.getLocation(REGISTER_PATH + "/"
               + "logjs.js"));

       // Load file load-css-file.js vào trang        
       jsLoader.require(resourceManager.getLocation(REGISTER_PATH + "/"
               + "load-css-file.js"));
       
       // Load file rap-handler.js vào trang
        jsLoader.require(resourceManager.getLocation(REGISTER_PATH + "/"
                  + "rap-handler.js"));
   }

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

       for (String fileName : FILENAMES) {
           // Sau khi đăng ký có thể truy cập trên trình duyệt:
           // (http://localhost:port/rwt-resources/logjs/abc.js )
           // logjs/abc.js
           String path = REGISTER_PATH + "/" + fileName;

           // Kiểm tra xem resource này đã được đang ký chưa.
           boolean isRegistered = resourceManager.isRegistered(path);

           if (!isRegistered) {
               ClassLoader classLoader = LogComposite.class.getClassLoader();
               // Đường dẫn thực của file. (Trong 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 {
                   // Đăng ký 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());
   }
}
Bạn cần phải xuất khẩu package org.o7planning.customwidget.logcomposite, để cho phép các plugin khác có thể sử dụng class LogComposite của plugin này.

5- Test LogComposite

Bạn cần tạo một Project để test LogComposite. Ở đây tôi tạo một ứng dụng RAP cơ bản.
  • File/New/Other...
Đăng ký sử dụng LogComposite plugin
Mở code class BasicEntryPoint, sửa đổi để cho phép bạn có thể thiết kế giao diện trên WindowBuilder.
    @Override
    protected void createContents(Composite parent) {
        
        // Sửa code giống dưới đây, để có thể thiết kế trên 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 đã có trên Palette, giống như các Widget khác, bạn có thể dễ dàng kéo thả nó vào giao diện thiết kế của bạn.
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();
   }

}

Chạy ứng dụng:

Nhấn phải chuột vào project "SimpleRAPApplication" chọn Run As/RAP Application
Lỗi trên cho thấy rằng bạn cần cấu hình RAP runtime, trước khi chạy ứng dụng.
Kết quả chạy trên RAP: