Transform RCP applications to web applications using RAP

Migrate with minor changes

The Rich Client Platform (RCP) enables you to build rich desktop applications. The Rich Ajax Platform (RAP) lets you build Ajax-enabled web applications using the Eclipse development model. With the assistance of RAP, existing RCP applications can be run as web applications with only minor changes. In this article, explore the key features of RAP, and follow example code for a simple HTML viewer to learn how to migrate RCP applications to the web using RAP.

Zhi Da Luo (luozd@cn.ibm.com), Software Engineer, IBM

Zhi Luo photoZhi Da Luo is a software engineer at the Emerging Technology Institute, IBM China Development Lab. Mr. Luo joined IBM in 2008. He has experience in program analysis, bytecode instrumentation, and Java concurrency programming. He is currently working on a static/run time analysis tool for Java parallel software. Mr. Luo holds a Master's degree in Software Engineering from Peking University in Beijing, China.



Wei Liu (lwlwei@cn.ibm.com), Software Engineer, IBM

Wei Liu photoWei Liu is a software engineer at the Emerging Technology Institute, IBM China Development Lab. Mr. Liu holds a Master's degree in Software Engineering from Peking University. He joined IBM in 2010 and has experience in Eclipse RCP and RAP, Java concurrency programming, and dynamic program analysis.



Ming Hai Wang (wminghai@cn.ibm.com), Software Engineer, iSoftStone

Ming Hai Wang photoMing Hai Wang, a software engineer, is skilled at plug-ins, RCP, and RAP development. For more than two years, he worked in RCP development. Mr. Wang also has experience migrating plug-ins from RCP to RAP, such as with the Multicore Software Development Kit for Java. Currently he is devoting more time to studying z/OS.



Raja Das (rajadas@us.ibm.com), Software Architect, IBM

Raja Das photoRaja Das is a software architect in the IBM Software Group. His current focus is developing libraries and frameworks for multicore/manycore systems. Previously, he was product architect for the WebSphere Partner Gateway. Dr. Das's interests include programming languages, parallel software, and systems.



19 April 2011

Also available in Chinese Japanese

Introduction

With the Eclipse Rich Client Platform (RCP), you can build applications. These rich applications are based on a dynamic plug-in model, and the UI is built using common toolkits and extension points. In open tools development, the Eclipse RCP is widely used.

The Rich Ajax Platform (RAP) project lets you build rich, Ajax-enabled web applications by using the Eclipse development model, plug-ins, and Java-only APIs. The objective of RAP is to enable the RCP applications to run in the web browser with as few modifications as possible.

RAP and RCP have the same interfaces, but their underlying implementations differ significantly. The RAP provides an alternative implementation of the SWT API called RWT, which runs on a servlet container like Tomcat. Clients can access it using standard web browsers, such as Internet Explorer or Chrome.

RAP and RCP applications are used in different user environments. As a desktop application, RCP works with only one user at a time. In contrast, a RAP application is a web-based program, and it can be accessed by multiple users simultaneously. RAP is based on Equinox, a server-side OSGi and runtime bundle. One OSGi instance per web application runs in an application scope. As opposed to RCP, RAP has to deal with user sessions and bundles that are shared among user sessions.

In this article, learn the key features of RAP and how to migrate an RCP application to the web using RAP. You can download the example code used in this article.

Single-sourcing techniques for RAP and RCP

RAP is popular for developing rich client applications and web applications from a single code base, also called single sourcing. You can reuse the existing RCP development skill and code base for web-based RAP applications. For the current version of RAP 1.3.x, 70%-90% of existing RCP code can be reused in the RAP environment without changes.

Dealing with different APIs and features

RCP and RAP have different APIs, features, UI libraries, extension-points, and user environments.

  • APIs: In RAP, Version 1.3.x, not all RCP APIs are available in RAP, such as GC (Graphics Context), MouseMove Events, and FileDialog. RAP has its own additional APIs for web-specific requirements—PhaseListener, ISessionStore, and so on. To deal with the different APIs, consider the following recommendations:
    • Define an interface in the host plug-in for the problem.
    • Define new classes that implement the interface in different fragments to solve the problem in a platform-specific manner.
    • Load the platform-specific implementation at runtime by Java reflection.

    Sample source code is provided in the "Single-sourcing example" section.

  • UI libraries: Since RCP and RAP have different target platforms, they have different underlying UI libraries. The host plug-in that contains the common code of RCP and RAP requires dependency on both platforms' UI libraries (for example, the org.eclipse.ui plug-in for RCP or the org.eclipse.rap.ui plug-in for RAP). Both need to be imported as required plug-ins of the host.

    To avoid compiling errors when using different target run times, enable the Optional check box under Properties, as shown in Figure 1. In this manner, the RAP/RCP runtime will load the correct library when it starts execution.

    Figure 1. Optional dependencies
    For optional dependencies
  • Extension-points (E-Ps): Currently, not all RCP E-Ps are available in RAP (for example, bindings and helpSupport). Because of the web-specific requirement, RAP includes some additional E-Ps, such as entrypoint and themes.

    The OSGi fragment was introduced to handle the different E-Ps and use the same code base for RCP and RAP. Fragments are special OSGi bundles; they extend another bundle and can be merged at runtime with the extended bundle. First, you define a host plug-in containing the common code of RCP and RAP. Then, you create two separate fragments for RAP and RCP, respectively, which include platform-specific code for RAP and RCP. At runtime, only one corresponding fragment (RAP or RCP) will be installed together with the host plug-in. Thus, platform-specific E-P extensions can be moved to the corresponding fragment, and the bundle structure stays intact.

  • Multi-user environment: The singleton pattern is widely used in RCP. However, for a web-based application that allows multiple users simultaneous access, a session-based singleton is required. RAP offers a base class—SessionSingletonBase—that provides a singleton implementation for each session and avoids the application state share among different users.

    A session singleton is an RWT-specific singleton that provides access to a unique instance within the session scope. In the context of one user session, getInstance(Class) will always return the same object. But, for different user sessions, the returned instances will be different.

    To implement a session singleton class, it should call SessionSingletonBase#getInstance(), which takes care of the proper scope of the singleton instances. Listing 1 shows this pattern. See Resources for RAP FAQs.

    Listing 1. Session singleton sample
    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);
        }
    }

Single-sourcing example

In this section, learn more about single sourcing with an example RCP application for an HTML viewer. You can download the sample code used in this article. The example has a view used to display an HTML file, which can be opened with the Open button on its toolbar, as shown in Figure 2:

Figure 2. RCP HTML viewer sample
Screen shot of RCP HTML viewer sample, with Open button identified

The example uses a browser widget to show the HTML file. To implement the function, you need to create a sample plug-in called com.ibm.msdk.example.

  1. Identify the platform-specific code in RCP.

    As mentioned, there is RAP/RCP platform-specific code in the sample plug-in. You have to first extract the common code from the sample project. By analyzing the code, a RAP-unsupported API is identified from the RCP HTML viewer: FileDialog#open (in the com.ibm.msdk.example.action package), as shown in Listing 2:

    Listing 2. Platform-specific code in 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);
    	}
    }

    In RAP, it makes sense to upload a file instead of opening it. The "RAP Upload Project" offers such an upload function; it can be downloaded from the RAP CVS repository (sandbox/org.eclipse.rwt.widgets.upload).

  2. Create RCP and RAP fragments.

    Because the com.ibm.msdk.example plug-in targets both RAP and RCP platforms, required bundles for the two platforms must be imported as required plug-ins. For example, the UI bundles—org.eclipse.ui (RCP) and org.eclipse.rap.ui (RAP)—are required. Make the different UI bundles' dependency Optional in the plug-in's Properties, as shown in Figure 3, to avoid compiling errors on different target platforms:

    Figure 3. Optional dependencies
    For optional dependencies

    The platform runtime environment (RCP or RAP) will load the correct bundles. Since it has two target platforms, you need to create two new fragments to hold the platform-specific code. As shown in Figure 4, the host plug-in property of fragments is set to the com.ibm.msdk.example plug-in:

    Figure 4. Set up host plug-in to com.ibm.msdk.example
    Set up the Host Plug-in to com.ibm.msdk.example
  3. Create abstract class to handle the special functions.

    RAP has no FileDialog widget. You have to use a new widget that can upload a file from the client side to the server. Fortunately, RAP CVS provides a customized upload widget that can be downloaded from the repository location URL :pserver:anonymous@dev.eclipse.org:/cvsroot/rt,find the widget from org.eclipse.rap/sandbox/org.eclipse.rwt.widgets.upload. To enable the uploaded widget to work correctly, the org.apache.commons.fileupload and org.apache.commons.io plug-ins are required. Both can be downloaded from the repository location URL :pserver:anonymous@dev.eclipse.org:/cvsroot/tools. Find the plug-ins from org.eclipse.orbit/. See Resources for more about how to use CVS for downloading.

    1. Add the required plug-ins to the RAP fragment, as shown in Figure 5:
      Figure 5. Add upload plug-ins to dependencies
      Add upload plug-ins to dependencies
    2. In the host plug-in, you need to create an abstract class to handle the special functions that are different across RAP and RCP platforms. Define an abstract method named doViewHtml(), as shown in Listing 3:
      Listing 3. Abstract class for 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. To invoke the doViewHtml() action, the caller part of the host plug-in needs modification. Listing 4 shows the method in the original class.
      Listing 4. Method in original class 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);
      	}	
      	...
      }

      You can get the new fragment action using Java reflection in the HtmlContentsView.java in the com.ibm.msdk.example plug-in, as shown in Listing 5. The full name of the subclasses created in the RCP fragment and the RAP fragment should be the same. In this manner, on different runtime platforms (RCP or RAP), the proper fragment would be loaded and the opportune doViewHtml() action would be invoked.

      Listing 5. Method in new class 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. Implement in RAP and RCP fragments.

    After defining an abstract class for the platform-specific function, you need to implement the abstract method differently in the RCP fragment and the RAP fragment, respectively. In the RCP fragment, create a new class that extends the abstract class AbstractViewHtmlAction, as shown in Listing 6. It implements the abstract method to open an HTML file with FileDialog.

    Listing 6. RCP implementation of AbstractViewHtmlAction
    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);
    	}
    	
    	...
    }

    The RAP fragment abstract method implementation is different from RCP. It uses an "upload" widget to transfer the HTML file to a web server and then opens it, as shown in Listing 7:

    Listing 7. RAP implementation of AbstractViewHtmlAction
    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. Multi-user support.

    The RAP platform provides a session singleton for accessing a unique instance within the session scope. The example makes a case for using a session singleton in RAP, and using a common singleton in RCP.

    1. Create a singleton class named ExampleSingleton in the host plug-in. An interface named ISingletonProvider and a class loader named ImplementationLoader are also created.
      Listing 8. Session singleton in host plug-in
      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();
      	}
      	
      }
      Listing 9. Interface in host plug-in
      public interface ISingletonProvider {
      
      	Object getInstanceInternal();
      	
      	/**
      	 * Fetches the singleton info,which is different 
      	 * between RCP platform and RAP platform.
      	 * @return
      	 */
      	String fetchSingletonInfo();
      }
      Listing 10. A loader
      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. Create ExampleSingleton subclasses. Implement the ISingletonProvider interface in both RAP and RCP fragments to provide different singleton implementation.
      Listing 11. Implementation of session singleton in RAP
      public class ExampleSingletonImpl implements ISingletonProvider {
      
      	public Object getInstanceInternal() {
      		return SessionSingletonBase.getInstance(ExampleSingleton.class);
      	}
      	...
      	...
      }
      Listing 12. Implementation of session singleton in 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!";
      	}
      }

Package and deploy the RAP application to Tomcat

In this section, you'll make a WAR file, deploy the file to Tomcat, and learn some troubleshooting tips if the deployed WAR file does not work.

Package a RAP application to WAR

Currently, Eclipse does not provide direct support for building a RAP application. For now, you need to make the WAR file using several Ant scripts and resource templates. The org.eclipse.rap.demo.feature in RAP's CVS shows how to create a WAR file that packs the org.eclipse.rap.demo project as a RAP application. You can modify it and make the feature project available for the HTML viewer sample.

  1. Download a team project set file from the online version of the RAP Developer Guide (Advanced Topics > WAR Deployment). (See Resources for a link.)
  2. Select File > Import > Team Project Set, and enter the location of the downloaded file.
  3. Click on Finish, and the necessary projects will be imported from CVS, including org.eclipse.equinox.http.servletbridge, org.eclipse.equinox.servletbridge, and org.eclipse.rap.demo.feature.
  4. Modify org.eclipse.rap.demo.feature's name to com.ibm.msdk.example.rap.feature, and add the following to the feature list (illustrated in Figure 6):
    • com.ibm.msdk.example
    • com.ibm.msdk.example.rap
    • org.apache.commons.fileupload
    • org.apache.commons.io
    • org.eclipse.rwt.widgets.upload

    If you don't need the RAP demo plug-in, remove org.eclipse.rap.demo from the feature list.

  5. Modify the feature's ID to com.ibm.msdk.example.rap.feature, as shown in Figure 6:
    Figure 6. Feature of the HTML viewer
    Feature of the HTML Viewer
  6. Configure and run the Ant task to build the whole HTML viewer RAP application, as follows:
    1. Open webappBuilder.xml and modify the property's value (in line 27) to com.ibm.msdk.example.rap.feature.
    2. Right-click on webappBuilder.xml and open the External Tools Dialog (Run As > External Tools Configuration).
    3. Double-click on Ant Build.
    4. Select the new entry, com.ibm.msdk.example.rap.feature webappBuilder.xml.
    5. In the JRE tab, adjust Runtime JRE to Run in the same JRE as the workspace. Otherwise, the script will not be able to use the PDE Ant Task for exporting our feature.
    6. Click on Run.
    7. You should get a BUILD SUCCESSFUL message on the console.

    Note, from the progress indicator, that the PDE Export task is still running in the background. Wait until it finishes.

  7. Generate the config.ini file:
    1. Open the Java file ConfigIniCreator.java in org.eclipse.rap.demo and modify line 39, as shown in Listing 13.
      Listing 13. Modified codes
      File file = new File( "build\\demo\\WEB-INF\\eclipse\\plug-ins" )
    2. Launch ConfigIniCreator as a Java application. (Text, similar to that in Listing 14, will be output to the Eclipse console.)
      Listing 14. Sample of 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. Copy all of them, and replace the old content of {Project root}/templates/WEB-INF/eclipse/config.ini.
    4. Remove @start from all of the RAP fragments (com.ibm.msdk.example.rap).
    5. For the first time, copy {Project root}/templates/WEB-INF/eclipse/config.ini to {Project root}/build/demo/WEB-INF/eclipse/configuration/, and replace the old one. If the feature.xml was modified, the config.ini needs to be regenerated.
    6. Create a new build.xml in the com.ibm.msdk.example.rap.feature/script directory, and copy the following script to it:
      Listing 15. Use ANT script to build the WAR file
      <?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. Run the build.xml as Ant Build in Eclipse. The WAR file sample.war of the HTML viewer will be generated in the project root directory, as shown in Figure 7:
      Figure 7. A sample.war in Eclipse workspace
      A sample.war in Eclipse workspace

Deploy the file to Tomcat

To deploy the sample.war file to Tomcat, you need to copy the sample.war to {TOMCAT_HOME}/webapps/. Start Tomcat, and access the HTML viewer with http://localhost:8080/sample/msdkExample, as shown in Figure 8:

Figure 8. Deploy sample.war to Tomcat
Deploy sample.war to Tomcat

Deployed WAR file does not work

You might encounter a scenario where the RAP is working at development time, but after deploying it to Tomcat or WebSphere, it does not work. If you have this situation, check the following:

  • If the RAP application uses the build.properties file, make sure they are exported and that all libraries that are used are listed on the plug-ins class path.
  • Enable the OSGi console by adding the modification in Listing 16 to the web.xml.
    Listing 16. Enable the console of OSGi framework
    <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>

    Type ss in the console (for example, Tomcat), and see if all bundles are started. If not, try starting them with start <bundle-id>. The stack traces may hint as to what is missing. Make sure that there is a org.eclipse.equinox.servletbridge.extensionbundle in the ss-listing with the state RESOVLED, as shown in Figure 9:

    Figure 9. Started bundles in Tomcat
    Started bundles in Tomcat
  • Make sure that the WAR does not contain:
    • The javax.servlet bundle. In the plug-in manifest, the javax.servlet must be listed in the Import-Package section, not in Require-Bundle.
    • The org.eclipse.update.configurator bundle.
    • The *.jetty.* bundles.
  • If the WAR file is redeployed, be sure to delete the work directory of the servlet engine (for example, <tomcat_install>/work/Catalina/localhost/work/<webapp_name> in Tomcat).

Conclusion

This article introduced the single-sourcing concept for RAP and RCP application development. You learned how to transform an existing RCP application to a web-based RAP application using single-sourcing techniques. RAP offers RCP developers a powerful tool to reuse your existing code base to create web-based applications. This article also supplied a method to package the RAP application to a WAR file, and showed how to deploy it to web containers.


Download

DescriptionNameSize
Project sampleworkspaceExample.zip57KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=648269
ArticleTitle=Transform RCP applications to web applications using RAP
publish-date=04192011