使用 RAP 将 RCP 应用程序转换成 web 应用程序

只需很小的修改即可实现迁移

Rich Client Platform (RCP) 可以让您创建富桌面应用程序。而 Rich Ajax Platform (RAP) 可以让您使用 Eclipse 开发模型,创建基于 Ajax 的 web 应用程序。通过 RAP 的帮助,已有的 RCP 应用程序只需很小的修改,就可以作为 web 应用程序运行。本文介绍 RAP 的关键特性,并通过一个简单的 HTML 查看器的示例代码展示如何使用 RAP 将 RCP 应用程序迁移为 web 应用程序。

Zhi Da Luo, 软件工程师, IBM

/developerworks/i/p-lzhida.jpgZhi Da Luo 是 IBM 中国开发实验室 Emerging Technology Institute 的一名软件工程师。他于 2008 年加入 IBM。他从事过程序分析、字节码检测(bytecode instrumentation)以及 Java 并发编程。他目前正在开发用于 Java 并行软件的静态/运行时分析工具。他拥有北京大学软件工程硕士学位。



Wei Liu, 软件工程师, IBM

/developerworks/i/p-wliu.jpgWei Liu 是 IBM 中国开发实验室 Emerging Technology Institute 的一名软件工程师。他拥有北京大学软件工程学士学位。他于 2010 年加入 IBM ,曾从事过 Eclipse RCP 和 RAP、Java 并发编程以及动态语言分析等工作。



Ming Hai Wang, 软件工程师, IBM

/developerworks/i/p-mhwang.jpgMing Hai Wang 是一名软件工程师,擅长插件开发、RCP 以及 RAP 开发。他拥有两年以上 RCP 开发经验。同时,他还拥有将 RCP 插件迁移到 RAP 上的经验, 例如 Multicore Software Development Kit for Java 使用经验。现在他正致力于研究 z/OS。



Raja Das, 软件架构师, IBM

http://www.ibm.com/developerworks/i/p-rdas.jpgRaja Das 是 IBM Software Group 的一位软件架构师。他目前主要负责为多核系统开发程序库和框架。之前,他是 WebSphere Partner Gateway 的产品架构师。Das 博士的兴趣包括程序设计语言、并行软件和系统。



2012 年 5 月 14 日

简介

通过使用 Eclipse Rich Client Platform (RCP),您可以构建应用程序。这些富应用程序都基于一个动态插件模型,并且 UI 都通过普通工具集和扩展点创建。 在开放工具开发中,Eclipse RCP 被广泛使用。

Rich Ajax Platform (RAP) 项目使您可以使用 Eclipse 开发模型、插件以及只面向 Java 的 API,来创建基于 Ajax 的富 web 应用程序。 RAP 的目标是尽可能不修改 RCP 应用程序,使其可以运行在 web 浏览器中。

RAP 和 RCP 有着相似的接口,但是它们底层的实现却大相径庭。RAP 提供了一个名为 RWT 的 SWT API 的替代实现,运行在例如 Tomcat 的 servlet 容器上。 客户端可以通过标准的浏览器来访问,例如 Internet Explorer 和 Chrome。

RAP 和 RCP 应用程序用于不同的用户环境。作为一个桌面应用程序,RCP 同时只能为一个用户工作。相反,由于 RAP 应用程序是一个基于 web 的应用程序,所以它可以同时被多个用户访问。RAP 基于 Equinox,这是一个服务端 OSGi 和运行时 bundle 。从应用程序的角度来看,每个 web 应用程序都运行了一个 OSGi 实例。 与 RCP 相反,RAP 必须处理用户会话,以及用户会话之间共享的多个 bundle。

在本文中,您将了解 RAP 的关键特性,以及如何使用 RAP 将 RCP 应用程序迁移为 web 应用程序。您可以 下载 本文使用的示例代码。

用于 RAP 和 RCP 的单一来源技术

RAP 之所以流行,是因为它基于单个代码基(也称为单一来源)开发富客户端应用和 web 应用程序。您可以重用已有的 RCP 开发技巧和代码基来开发基于 web 的 RAP 应用程序。对于当前的 RAP 1.3.x 版本,70%-90% 的现有 RCP 代码无需任何修改即可在 RAP 环境中重用。

处理不同的 API 和特性

RCP 和 RAP 有着不同的 API、特性、UI 库、扩展点以及用户环境。

  • APIs:在 RAP 1.3.x 版本中,并非所有 RCP API 都受到支持,例如 GC(Graphics Context)、MouseMove Events 以及 FileDialog。由于 web 的特殊需求,RAP 有自己的 APIs,例如 PhaseListenerISessionStore 等等。为了处理这些不同的 API,请考虑以下建议:
    • 在主机插件中,为该问题定义一个接口。
    • 定义一些在不同的片段中实现该接口的新类,以特定于平台的方式解决该问题。
    • 通过 Java 反射在运行时动态加载特定于平台的实现。

    单一来源示例” 小节提供了示例源代码。

  • UI 库:由于 RCP 和 RAP 有着不同的目标平台,因此它们底层的 UI 库也不同。包含 RCP 和 RAP 通用代码的主机插件,需要依赖 于双方的平台 UI 库(例如,用于 RCP 的 org.eclipse.ui 插件,或者用于 RAP 的 org.eclipse.rap.ui 插件)。二者都需要作为主机必不可少的插件被导入。

    当使用不同的目标运行时的时候,为了避免编译错误,请勾选 Properties 下的 Optional 复选框 ,如 图 1 所示。在这种方式下,RAP/RCP 运行时会在开始执行时加载正确的库。

    图 1. Optional 依赖项
    可选依赖项
  • Extension-points (E-Ps):当前,在 RAP 中并不是所有的 RCP 扩展点都可以使用(例如 bindingshelpSupport)。由于 web 特有的需求,RAP 包括一些额外的扩展点,例如 entrypointthemes

    因此,引入了 OSGi 片段来处理不同的扩展点,并且对 RCP 和 RAP 使用相同的代码库。片段是一种特殊的 OSGi bundle,它们可以扩展另一个 bundle,并在运行时与被扩展的 bundle 相合并。首先,您需要定义一个包含 RCP 和 RAP 通用代码的主机插件。然后,您分别为 RAP 和 RCP 创建两个片段。在运行时,只有一个相应的片段(RAP 或者 RCP)会与主机插件一同安装。这样,平台特有的扩展点就可以被 转移到相应的片段,并且 bundle 结构仍保持不变。

  • 多用户环境: 在 RCP 中广泛使用了单例模式。但是,对于允许多用户同时访问的基于 web 的应用程序来说,需要一个基于会话的单例。 RAP 提供了一个基类 —SessionSingletonBase— 该基类为每个会话提供了一个单例实现,从而避免了多个 用户之间共享应用程序状态。

    会话单例 是一个 RWT 特有的单例,提供了在会话作用域中对一个唯一单例的访问。在一个用户会话上下文中, getInstance(Class) 总是会返回相同的对象。但是,对于不同的用户会话来说,返回的实例总是不同的。

    为了实现一个会话单例类,应该调用 SessionSingletonBase#getInstance(),该方法负责为单例实例指定合适的作用域。清单 1 说明了该模式。请参考 参考资料 中的 ARP FAQs。

    清单 1. 会话单例示例
    package com.ibm.msdk.ui.configWas;
    
    import org.eclipse.rwt.SessionSingletonBase;
    
    public class SampleSessionSingleton{
        private SampleSessionSingleton(){}
    	
        public static SampleSessionSingleton getInstance(){
    	    return (SampleSessionSingleton)SessionSingletonBase
    		        .getInstance(SampleSessionSingleton.class);
        }
    }

单一来源示例

在本节中,我们会通过一个 HTML 查看器的 RCP 应用程序,了解更多有关单一来源的内容。您可以 下载 本文使用的示例代码。示例程序有一个用于显示 HTML 文件的视图,可以通过工具栏中的 Open 按钮打开,如 图 2 所示:

图 2. RCP HTML 查看器示例程序
RCP HTML 查看器示例程序的截图,带有一个 Open 按钮

示例程序使用了一个浏览器小部件来显示 HTML 文件。为了实现该功能,您需要创建一个名为 com.ibm.msdk.example 的示例插件。

  1. 标识 RCP 中平台特有的代码。

    正如上文所提到的,在示例插件中有 RAP/RCP 平台特有的代码。您必须首先将通用代码从示例工程中解压出来。 通过分析代码,可以从 RCP HTML 查看器中标识出 RAP 不支持的 API:FileDialog#open(在 com.ibm.msdk.example.action 包中),如清单 2 所示:

    清单 2. RCP 平台特有代码
    import org.eclipse.swt.widgets.Shell;
    import org.eclipse.ui.IWorkbenchWindow;
    
    import com.ibm.msdk.example.utils.HtmlFileReader;
    
    public class ImportHtmlAction extends Action {
    	
    	public static final String ACT_IMPORT_FILE = "ACT_IMPORT_FILE";
    	
    	private IWorkbenchWindow workbenchWindow;
    	
    /**
    	 * Constructs an instance of <code>ImportHtmlAction</code> by given 
    	 * text and <code>IWorkbenchWindow</code>.
    	 * @param text
    	 * @param workbenchWindow
    	 */
    	public ImportHtmlAction(String text,IWorkbenchWindow workbenchWindow){
    		super(text);
    		this.workbenchWindow = workbenchWindow;
    	}
    
    	/**
    	 * Creates a file dialog for opening a new file,calls the method 
    	 * {@link HtmlFileReader#fetchHtmlContents(String)} to fetch the contents 
    	 * of the passed HTML file. Then fires the listener of <code>HTMLView</code> 
    	 * to refresh the contents of the view.
    	 */
    	@Override
    	public void run(){
    		Shell shell = workbenchWindow.getShell();
    		
    		FileDialog fileDialog = new FileDialog(shell,SWT.OPEN);
    		String filePath = fileDialog.open();
    		String htmlContents = null;
    		
    		if(filePath != null)
    			htmlContents = HtmlFileReader.fetchHtmlContents(filePath);
    		
    		firePropertyChange(ACT_IMPORT_FILE, null, htmlContents);
    	}
    }

    在 RAP 中,上传 一个文件比打开它更有实际意义。“RAP Upload Project” 提供了这样一个上传功能,可以从 RAP CVS 存储库(sandbox/org.eclipse.rwt.widgets.upload)下载。

  2. 创建 RCP 和 RAP 片段。

    由于 com.ibm.msdk.example 插件以 RAP 和 RCP 两个平台为目标,所以两个平台所需要的 bundle 必须作为不可或缺的插件 导入。例如,UI bundle —org.eclipse.ui(RCP) 和 org.eclipse.rap.ui(RAP)— 都是必需的。如 图 3 所示,在插件的 Properties 中,将不同 UI bundle 的依赖项设置为 Optional,以避免在不同目标平台上产生编译错误:

    图 3. 可选依赖项
    可选依赖项

    平台运行环境(RCP 或者 RAP)会加载正确的 bundle 。因为它有两个目标平台,所以您需要创建两个新的片段来处理平台特有的代码。如 图 4 所示,片段的主机插件属性被设置为 com.ibm.msdk.example 插件:

    图 4. 设置主机插件为 com.ibm.msdk.example
    设置主机插件为 com.ibm.msdk.example
  3. 创建抽象类来处理特有的功能。

    RAP 没有 FileDialog 小部件。您必须使用一个可以将一个文件从客户端上传到服务端的新小部件。幸运的是,RAP CVS 提供了一个自定义的上传小部件,可以从 存储库地址 URL:pserver:anonymous@dev.eclipse.org:/cvsroot/rt 下载,并从 org.eclipse.rap/sandbox/org.eclipse.rwt.widgets.upload 找到该小部件。 为了能让上传小部件正确工作,需要 org.apache.commons.fileuploadorg.apache.commons.io 两个插件。二者均可以从存储库地址 URL:pserver:anonymous@dev.eclipse.org:/cvsroot/tools 处下载。从 org.eclipse.orbit/ 找到插件。 更多有关如何使用 CVS 来下载的信息请参考 参考资料

    1. 图 5 所示,向 RAP 片段添加必需的插件:
      图 5. 向依赖项添加上传插件
      向依赖项添加上传插件
    2. 在主机插件中,您需要创建一个抽象类来处理 RAP 和 RCP 平台之间不同的特有功能。如 清单 3 所示,定义一个名为 doViewHtml() 的抽象方法:
      清单 3. Action 抽象类
      /**
       * 
       */
      package com.ibm.msdk.example.actions;
      
      import org.eclipse.jface.action.Action;
      import org.eclipse.ui.IWorkbenchWindow;
      
      /**
       * @author Minghai.Wang
       * @version 1.0 2010-10-26
       * @since JDK1.6
       * @email wminghai@cn.ibmm.com
       */
      public abstract class AbstractViewHtmlAction extends Action {
      
      	protected IWorkbenchWindow workbenchWindow;
      
      	public static final String ACT_IMPORT_FILE = "ACT_IMPORT_FILE";
      
      	public static final String INSTANCE_CLASS = "com.ibm.msdk.example
      				.actions.ViewHtmlAction";
      
      	/**
      	 * Constructs an instance of <code>AImportHtmlAction</code> by given
      	 * <code>IWorkbenchWindow</code>.
      	 * 
      	 * @param workbenchWindow
      	 */
      	public AbstractViewHtmlAction(IWorkbenchWindow workbenchWindow) {
      		this.workbenchWindow = workbenchWindow;
      	}
      
      	public void run() {
      		doViewHtml();
      	}
      
      	/**
      	 * Abstracts the method for viewing the given HTML file.
      	 * <p>
      	 * It is different between RCP platform and RAP platform.
      	 * <ul>
      	 * <li>In the RCP fragment,open a file dialog and select a HTML file that
      	 * will be parsed, and then show it.</li>
      	 * <li>In the RAP fragment,upload a parsing HTML file, then show it.
      	 * </ul>
      	 */
      	 
      	public abstract void doViewHtml();
      	 
      	/**
      	 * @return the workbenchWindow
      	 */
      	public IWorkbenchWindow getWorkbenchWindow() {
      		return workbenchWindow;
      	}
      
      	/**
      	 * @param workbenchWindow
      	 *            the workbenchWindow to set
      	 */
      	public void setWorkbenchWindow(IWorkbenchWindow workbenchWindow) {
      		this.workbenchWindow = workbenchWindow;
      	}
      
      }
    3. 为了避免调用 doViewHtml() 方法,需要修改主机插件调用部分的代码。 清单 4 显示了原有类中的该方法。
      清单 4. 在原有 HtmlContentsView 类中的方法
      public class HtmlContentsView extends ViewPart implements
      		IPropertyChangeListener {
      		
          ...
          ...
      	/**
      	 * Fills the tool bar of the HTML view with a importing HTML action. A
      	 * listener is added to the created action of importing HTML view.
      	 */
      	protected void fillToolBarAction() {
      		ImportHtmlAction importHtmlAct = new ImportHtmlAction("Open...",
      				getViewSite().getWorkbenchWindow());
      		IToolBarManager toolBarManager = getViewSite().getActionBars()
      				.getToolBarManager();
      		toolBarManager.add(importHtmlAct);
      
      		importHtmlAct.addPropertyChangeListener(this);
      	}	
      	...
      }

      清单 5 所示,您可以使用 com.ibm.msdk.example 插件中 HtmlContentsView.java 中的 Java 反射来获取新的片段方法。在 RCP 片段和 RAP 片段中创建的子类全名应该是相同的。按照这种方式,在不同的运行平台(RCP 或者 RAP)上,都可以加载合适的片段,并且调用合适的 doViewHtml() 方法。

      清单 5. 新类 HtmlContentsView 中的方法
      public class HtmlContentsView extends ViewPart implements
      		IPropertyChangeListener {		
          ...
      	/**
      	 * Fills an instance of <code>AbstractViewHtmlAction</code> 
      	 * that is created by Java reflection.
      	 */
          protected void fillToolBarAction() {
      	  try {
      		Class<?> clazz = Class
      		   .forName(AbstractViewHtmlAction.INSTANCE_CLASS);
      		Constructor<?> cons = clazz
      		   .getConstructor(IWorkbenchWindow.class);
      		AbstractViewHtmlAction viewHtmlAction = (AbstractViewHtmlAction) 
      		cons.newInstance(getViewSite().getWorkbenchWindow());
      
      		IToolBarManager toolBarManager = getViewSite().getActionBars()
      		   .getToolBarManager();
      		toolBarManager.add(viewHtmlAction);
      
      		viewHtmlAction.addPropertyChangeListener(this);
      			
      //		Add an action for viewing singleton info.
      		ViewSingletoInfoAction viewSingleton = new ViewSingletoInfoAction(
      		    getViewSite().getWorkbenchWindow());
      		toolBarManager.add(viewSingleton);
      	  } catch (ClassNotFoundException e) {
      		  e.printStackTrace();
      	  } catch (Exception e) {
      		  e.printStackTrace();
      	  } 
      	}	
      	...
      }
  4. 在 RAP 和 RCP 片段中的实现。

    在为平台特有的方法定义一个抽象类之后,您需要在 RCP 片段和 RAP 片段中分别添加抽象方法的不同实现。 在 RCP 片段中,创建一个继承自抽象类 AbstractViewHtmlAction 的新类,如 清单 6 所示。它实现了抽象方法,通过文件对话框来打开一个 HTML 文件。

    清单 6. AbstractViewHtmlAction 的 RCP 实现
    public class ViewHtmlAction extends AbstractViewHtmlAction {
    	...
    
    	/**
    	 * Creates a file dialog for opening a new file,calls the method
    	 * {@link HtmlFileReader#fetchHtmlContents(String)} to fetch the 
    	 * contents of the passed HTML file.Then fires the listener of 
    	 * <code>HTMLView</code> to refresh the contents of the view.
    	 */
    	@Override
    	public void doViewHtml() {
    		Shell shell = workbenchWindow.getShell();
    		FileDialog fileDialog = new FileDialog(shell,SWT.OPEN);
    		String filePath = fileDialog.open();
    		String htmlContents = null;
    		if(filePath != null)
    			htmlContents = HtmlFileReader.fetchHtmlContents(filePath);
    		
    		firePropertyChange(ACT_IMPORT_FILE, null, htmlContents);
    	}
    	
    	...
    }

    RAP 片段抽象方法的实现与 RCP 不同。如 清单 7 所示,它使用了一个 “上传” 小部件将 HTML 文件传输给 web 服务器,然后打开该文件:

    清单 7. AbstractViewHtmlAction 的 RAP 实现
    public class ViewHtmlAction extends AbstractViewHtmlAction {
    	...
    
    	/**
    	 * Opens a new shell for uploading a HTML file,
    	 * creates the widgets.
    	 */
    	@Override
    	public void doViewHtml() {
    		Display display = Display.getDefault();
    		Shell shell = new Shell(display, SWT.PRIMARY_MODAL 
    				| SWT.CLOSE | SWT.RESIZE);
    		shell.setText("Upload a HTML file");
    		shell.setLayout(new GridLayout());
    		shell.setSize(450, 60);
    //		Creates the control in the shell.
    		createControl(shell);
    		shell.layout();
    		shell.open();
    		while (!shell.isDisposed()) {
    			if (!display.readAndDispatch())
    				display.sleep();
    		}
    	}
    	
    	/**
    	 * 
    	 * @param shell
    	 */
    	protected void createControl(final Shell shell){
    		Upload upload = new Upload(shell,SWT.NONE,Upload.SHOW_UPLOAD_BUTTON);
    		upload.setUploadButtonText("Upload");
    		upload.setBrowseButtonText("Location...");
    		GridDataFactory.fillDefaults().grab(true, false).applyTo(upload);
    		
    		upload.addUploadListener(new UploadListener(){
    			...
    			...
    		});
    	}	
    	...
    }
  5. 多用户支持。

    RAP 平台提供了一个会话单例,用于在会话作用域中访问一个唯一的实例。 示例程序演示了如何在 RAP 中使用一个会话单例,以及在 RCP 中使用一个通用单例。

    1. 在主机插件中创建一个名为 ExampleSingleton 的单例类。同时创建一个名为 ISingletonProvider 的接口以及一个名为 ImplementationLoader 的类加载器。
      清单 8. 在主机插件中的会话单例
      public class ExampleSingleton {
      
      	private final static ISingletonProvider PROVIDER;
      	static{
      		PROVIDER = (ISingletonProvider) ImplementationLoader
      				.newInstance(ExampleSingleton.class);
      	}
      	
      	/**
      	 * Gets the instance of <code>ExampleSingleton</code> which has 
      	 * different implementations in RAP and RCP fragment.
      	 * @return
      	 */
      	public static ExampleSingleton getInstance(){
      		return (ExampleSingleton)PROVIDER.getInstanceInternal();
      	}
      	
      	public String fetchSingtonInfo(){
      		return PROVIDER.fetchSingletonInfo();
      	}
      	
      }
      清单 9. 主机插件中的接口
      public interface ISingletonProvider {
      
      	Object getInstanceInternal();
      	
      	/**
      	 * Fetches the singleton info,which is different 
      	 * between RCP platform and RAP platform.
      	 * @return
      	 */
      	String fetchSingletonInfo();
      }
      清单 10. 一个加载器
      public class ImplementationLoader {
      
      	public static Object newInstance(final Class<?> type){
      		String name = type.getName();
      		Object result = null;
      		try {
      			result = type.getClassLoader()
      			    .loadClass(name + "Impl").newInstance();
      		} catch (Exception e) {
      			e.printStackTrace();
      		}
      		return result;
      	}
      }
    2. 创建 ExampleSingleton 的子类。在 RAP 和 RCP 片段中均实现 ISingletonProvider 接口,以提供不同的单例实现。
      清单 11. 在 RAP 中会话单例的实现
      public class ExampleSingletonImpl implements ISingletonProvider {
      
      	public Object getInstanceInternal() {
      		return SessionSingletonBase.getInstance(ExampleSingleton.class);
      	}
      	...
      	...
      }
      清单 12. 在 RCP 中会话单例的实现
      public class ExampleSingletonImpl implements ISingletonProvider {
      	
      	private static ExampleSingleton singleton;
      	
      	/* (non-Javadoc)
      	 * @see com.ibm.msdk.example.singleton
      	 * .ISingletonProvider#getInstanceInternal()
      	 */
      	@Override
      	public Object getInstanceInternal() {
      		if(singleton == null)
      			singleton = new ExampleSingleton();
      		return singleton;
      	}
      
      	/* (non-Javadoc)
      	 * @see com.ibm.msdk.example.singleton
      	 * .ISingletonProvider#fetchSingletonInfo()
      	 */
      	@Override
      	public String fetchSingletonInfo() {
      		return "This message is from RCP fragment!";
      	}
      }

打包和将 RAP 应用程序部署到 Tomcat

在本节中,您将会创建一个 WAR 文件,将其部署到 Tomcat,并且了解一些部署 WAR 文件失败时的解决办法。

将一个 RAP 应用程序打包为 WAR 文件

当前,Eclipse 并不直接支持构建一个 RAP 应用程序。对现在来说,您可以使用一些 Ant 脚本和资源模板来创建 WAR 文件。RAP CVS 中的 org.eclipse.rap.demo.feature 说明了如何将 org.eclipse.rap.demo 作为一个 RAP 应用程序打包成一个 WAR 文件。 您可以对其进行修改,使其可以用于 HTML 查看器示例程序。

  1. 从 RAP Developer Guide (Advanced Topics > WAR Deployment) 的在线版本下载一个 团队项目集(team project set) 文件。(请参考 参考资料 中的链接。)
  2. 选择 File > Import > Team Project Set,然后输入下载文件的位置。
  3. 单击 Finish,然后将从 CVS 导入必需的项目,包括 org.eclipse.equinox.http.servletbridge、org.eclipse.equinox.servletbridge 以及 org.eclipse.rap.demo.feature。
  4. 将 org.eclipse.rap.demo.feature 的名称修改为 com.ibm.msdk.example.rap.feature,并且将以下内容添加到功能列表中(如 图 6 所示):
    • com.ibm.msdk.example
    • com.ibm.msdk.example.rap
    • org.apache.commons.fileupload
    • org.apache.commons.io
    • org.eclipse.rwt.widgets.upload

    如果您不需要 RAP 演示程序插件,从功能列表中删除 org.eclipse.rap.demo。

  5. 将功能的 ID 修改为 com.ibm.msdk.example.rap.feature,如 图 6 所示:
    图 6. HTML 查看器功能
    HTML 查看器功能
  6. 配置并运行 Ant 命令以构建整个 HTML 查看器 RAP 应用程序,如下所示:
    1. 打开 webappBuilder.xml 并修改属性的值(在第 27 行)为 com.ibm.msdk.example.rap.feature
    2. 右键单击 webappBuilder.xml 并打开 External Tools Dialog(Run As > External Tools Configuration)。
    3. 双击 Ant Build
    4. 选中新的实体,com.ibm.msdk.example.rap.feature webappBuilder.xml
    5. 在 JRE 选项卡中,调整 Runtime JRERun in the same JRE as the workspace。否则,脚本无法使用 PDE Ant 命令来导出我们的功能。
    6. 单击 Run
    7. 您应该在控制台上看到一条 BUILD SUCCESSFUL 消息。

    注意,从进度指示条看,会发现 PDE 导出命令仍然在后台运行,等待它完成。

  7. 生成 config.ini 文件:
    1. 打开 org.eclipse.rap.demo 中的 ConfigIniCreator.java,修改第 39 行,如 清单 13 所示。
      清单 13. 修改代码
      File file = new File( "build\\demo\\WEB-INF\\eclipse\\plug-ins" )
    2. 将 ConfigIniCreator 作为一个 Java 应用程序启动。(在 Eclipse 控制台上会输出与 清单 14 中相似的文本。)
      清单 14. Config.ini 示例
      #Eclipse Runtime Configuration File
      osgi.bundles= com.ibm.icu.base@start,\
      com.ibm.msdk.example.rap,\
      com.ibm.msdk.example@start,\
      org.apache.commons.fileupload@start,\
      org.apache.commons.io@start,\
      org.eclipse.core.commands@start,\
      org.eclipse.core.contenttype@start,\
      org.eclipse.core.databinding.observable@start,\
      org.eclipse.core.databinding.property@start,\
      org.eclipse.core.databinding@start,\
      org.eclipse.core.expressions@start,\
      org.eclipse.core.jobs@start,\
      org.eclipse.core.runtime@start,\
      org.eclipse.equinox.app@start,\
      org.eclipse.equinox.common@2:start,\
      org.eclipse.equinox.http.registry@start,\
      org.eclipse.equinox.http.servletbridge@start,\
      org.eclipse.equinox.http.servlet@start,\
      org.eclipse.equinox.preferences@start,\
      org.eclipse.equinox.registry@start,\
      org.eclipse.help@start,\
      org.eclipse.osgi.services@start,\
      org.eclipse.rap.demo@start,\
      org.eclipse.rap.jface.databinding@start,\
      org.eclipse.rap.jface@start,\
      org.eclipse.rap.rwt.q07,\
      org.eclipse.rap.rwt@start,\
      org.eclipse.rap.ui.views@start,\
      org.eclipse.rap.ui.workbench@start,\
      org.eclipse.rap.ui@start,\
      org.eclipse.rwt.widgets.upload@start,\
      org.eclipse.equinox.servletbridge.extensionbundle
      osgi.bundles.defaultStartLevel=4
    3. 将它们复制并替换 {Project root}/templates/WEB-INF/eclipse/config.ini 中原有的内容。
    4. 从所有 RAP 片段(com.ibm.msdk.example.rap)中删除 @start
    5. 对于第一次而言,将 {Project root}/templates/WEB-INF/eclipse/config.ini 复制到 {Project root}/build/demo/WEB-INF/eclipse/configuration/,然后替换原有的文件。如果 feature.xml 已经被修改,那么需要重新生成 config.ini。
    6. 在 com.ibm.msdk.example.rap.feature/script 目录中创建一个新的 build.xml 文件,然后向其中复制以下脚本:
      清单 15. 使用 ANT 脚本来创建 WAR 文件
      <?xml version="1.0"?>
      <project name="warbuild" default="war.gen">
        <property name="war.name" value="sample.war"/>
        <target name="war.gen" depends="clean">
          <war warfile="../${war.name}" webxml="../build/msdk/WEB-INF/web.xml">
            <lib dir="../build/msdk/WEB-INF/lib" />
            <lib dir="../build/msdk/WEB-INF/eclipse" prefix="WEB-INF/eclipse" />
          </war>
        </target>
        <target name="clean">
          <delete file="../${war.name}"/>
        </target>
      </project>
    7. 在 Eclipse 中将 build.xml 作为 Ant Build 运行。在工程根目录下会生成 HTML 查看器的 WAR 文件 sample.war,如 图 7 所示:
      图 7. Eclipse 工作区中的 sample.war
      Eclipse 工作区中的 sample.war

将文件部署到 Tomcat 中

为了将 sample.war 文件部署到 Tomcat,您需要复制 sample.war 到 {TOMCAT_HOME}/webapps/。启动 Tomcat,然后通过 http://localhost:8080/sample/msdkExample 访问 HTML 查看器,如 图 8 所示:

图 8. 将 sample.war 部署到 Tomcat
将 sample.war 部署到 Tomcat

部署的 WAR 文件无法正常工作

您也许会遇到这样一种情况,在开发时 RAP 可以正常工作,但是部署到 Tomcat 或者 WebSphere 后就无法工作。如果您有这种情况,检查以下内容:

  • 如果 RAP 应用程序使用 build.properties 文件,确定它们已经被导出,并且所有使用的库都已经被列举在插件类路径上。
  • 通过对 web.xml 中进行以下修改,来启用 OSGi 清单 16
    清单 16. 启用 OSGi 框架的控制台
    <init-param>
    <param-name>commandline</param-name>
    <param-value>-console</param-value>
    </init-param> 
    <!-- 
    <init-param>
    <param-name>commandline</param-name>
    <param-value>-registryMultiLanguage</param-value> 
    </init-param>
    -->
    <init-param>
    <param-name>enableFrameworkControls</param-name>
    <param-value>true</param-value>
    </init-param>

    在控制台(例如,Tomcat)中输入 ss, 然后观察是否所有 bundle 都已启动。如果没有,试着使用 start <bundle-id> 来启动它们。 堆栈跟踪也许能提示缺少哪些 bundle。确定 ss 命令结果列表中的 org.eclipse.equinox.servletbridge.extensionbundleRESOVLED 状态,如 图 9 所示:

    图 9. 在 Tomcat 中启动 bundle
    在 Tomcat 中启动 bundle
  • 确定 WAR 中不包含:
    • javax.servlet bundle。在插件清单(manifest)中,javax.servlet 必须出现在 Import-Package 部分中,而不能在 Require-Bundle 中。
    • org.eclipse.update.configurator bundle。
    • *.jetty.* bundle。
  • 如果 WAR 文件被重新部署,确定删除 servlet 引擎的工作目录(例如,Tomcat 中的 <tomcat_install>/work/Catalina/localhost/work/<webapp_name> )。

结束语

本文介绍了 RAP 和 RCP 应用程序部署中的单一来源概念。您了解了如何使用单一来源技术, 将一个已有的 RCP 应用程序转换为一个基于 web 的 RAP 应用程序。RAP 为 RCP 开发人员提供了一个强大的工具,通过重用您已有的代码基,来创建 基于 web 的应用程序。本文还提供了一个将 RAP 应用程序打包为 WAR 文件的方法,并且演示了如何将其部署到 web 容器中。


下载

描述名字大小
项目样例workspaceExample.zip57KB

参考资料

学习

获得产品和技术

  • RAP Tooling 和 RAP Runtime:下载 RAP Tooling 和 RAP Runtime。
  • 用于 RCP 和 RAP 开发人员的 Eclipse:下载用于 RCP 和 RAP 开发人员的 Eclipse — 为那些希望创建 Eclipse 插件、富客户端或者富 Ajax 应用程序(RCP + RAP)的开发人员,提供一个完整的工具集合,以及 Mylyn 和一个 XML 编辑器。

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=815534
ArticleTitle=使用 RAP 将 RCP 应用程序转换成 web 应用程序
publish-date=05142012