Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "RAP/FAQ"

< RAP
(Where to obtain language packs?)
(Troubleshooting)
(210 intermediate revisions by 17 users not shown)
Line 1: Line 1:
| [[RAP|RAP wiki home]] | [http://eclipse.org/rap RAP project home] |
+
This FAQ is always evolving. Please also scan the [http://dev.eclipse.org/newslists/news.eclipse.technology.rap/maillist.html RAP newsgroup archives] for answers.
  
This FAQ is still developing.
+
== Writing RAP Applications  ==
Please also scan the [http://dev.eclipse.org/newslists/news.eclipse.technology.rap/maillist.html RAP newsgroup archives] for answers.
+
  
== Getting Started ==
+
=== I found a bug in RAP. Where should I report it?  ===
  
=== I found a bug in RAP. Where should I report it? ===
+
Please report bugs or feature request in the Eclipse Bugzilla. See our [http://eclipse.org/rap/bugs/ Bugs page] for details.
  
The best way to report a problem or request a new feature in RAP is to use the Bugzilla system. Please follow the instructions in [http://wiki.eclipse.org/RAP_Bug_Reporting_Howto RAP Bug Reporting Howto] when submitting a bug report. More details are provided on the RAP Project [http://www.eclipse.org/rap/bugs.php Bugs page].
+
=== How to create a fullscreen application  ===
  
== Writing RAP Applications ==
+
*use the style-bit SWT.NO_TRIM before creating the Shell. This will remove the title-bar, the minimize/maximize/close buttons and the ability for manually resizing the window.  
 
+
*overwrite the WorkbenchWindowAdvisor.postWindowCreate() method. Here we set the state of the Shell to maximized.  
=== How to create a fullscreen application ===
+
*optional: depending on the requirements and complexity of our application it may be desirable to hide the menu bar by calling getWindowConfigurer().setShowMenuBar( false ). This is usually a good choice for small applications where the menu bar may not be needed.
* use the style-bit SWT.NO_TRIM before creating the Shell. This will remove the title-bar, the minimize/maximize/close buttons and the ability for manually resizing the window.
+
* overwrite the WorkbenchWindowAdvisor.postWindowCreate() method. Here we set the state of the Shell to maximized.
+
* optional: depending on the requirements and complexity of our application it may be desirable to hide the menu bar by calling getWindowConfigurer().setShowMenuBar( false ). This is usually a good choice for small applications where the menu bar may not be needed.
+
  
 
<source lang="java">
 
<source lang="java">
Line 34: Line 30:
 
}
 
}
  
</source>
+
</source> Take a look [http://eclipsesource.com/blogs/2007/11/12/hiding-the-window-in-rap-applications/ here] for a more detailed discussion of the topic.  
Take a look [http://eclipsesource.com/blogs/2007/11/12/hiding-the-window-in-rap-applications/ here] for a more detailed discussion of the topic.
+
 
 +
=== How do I add an applet / flash / an existing Javascript libary  ===
 +
 
 +
*A very simplistic approach is to create a html page (or a servlet) containing your applet / flash / JS. You can simply use the browser widget to load that page within your application.
 +
 
 +
=== How to access the request parameters?  ===
  
=== How do I add an applet / flash / an existing Javascript libary ===
+
Let's suppose that the request URI looks like the following:
* A very simplistic approach is to create a html page (or a servlet) containing your applet / flash / JS. You can simply use the browser widget to load that page within your application.
+
  
* A tighter integration can be achieved by developing a custom widget as explained here (integrating GMap): [http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.rap.help/help/html/advanced/custom-widget.html custom widget tutorial]
+
<code></code>
  
=== How to access the request parameters? ===
+
  <nowiki>http://www.example.com:8080/foo?var1=value1&var2=value2</nowiki>
  
Let's suppose that the request URI looks like the following:
+
<br>
  
<code>
+
To snippet below will retrieve the values of request parameters var1 and var2. <code></code>  
  <nowiki>http://www.example.com:8080/rap?startup=foo&var1=value1&var2=value2</nowiki>
+
</code>
+
  
To snippet below will retrieve the values of request parameters var1 and var2.
 
<code>
 
 
     HttpServletRequest request = RWT.getRequest();
 
     HttpServletRequest request = RWT.getRequest();
 
     String var1 = request.getParameter( "var1" );
 
     String var1 = request.getParameter( "var1" );
 
     String var2 = request.getParameter( "var2" );
 
     String var2 = request.getParameter( "var2" );
</code>
 
  
=== How to access the HTTP session / session store? ===
+
<br>
In RAP, the sessoin store is the preferred way to store information in the session context. However it also provides access to the HTTP session (see the code below). For working with session singletons, please use the <code>SessionSingletonBase</code> class.
+
  
Access the HTTP session / session store from the UI thread:
+
=== How to access the HTTP session / session store?  ===
<code lang="java">HttpSession session = RWT.getSessionStore().getHttpSession();</code>
+
  
Access the HTTP session / session store from a background thread:
+
'''Applies to RAP < 2.0'''
<source lang="java">
+
 
 +
In RAP, the sessoin store is the preferred way to store information in the session context. However it also provides access to the HTTP session (see the code below). For working with session singletons, please use the <code>SessionSingletonBase</code> class.
 +
 
 +
Access the HTTP session / session store from the UI thread: <code lang="java">HttpSession session = RWT.getSessionStore().getHttpSession();</code>
 +
 
 +
Access the HTTP session / session store from a background thread: <source lang="java">
 
final Display display = new Display();
 
final Display display = new Display();
 
final Runnable bgRunnable = new Runnable() {
 
final Runnable bgRunnable = new Runnable() {
Line 91: Line 89:
 
</source>
 
</source>
  
=== No context available outside of the request service lifecycle ===
+
'''Applies to RAP >= 2.0'''
  
Why am I getting the exception <code>java.lang.IllegalStateException: No context available outside of the request service lifecycle.</code>?
+
Access the HTTP session / UI session from a background thread: <source lang="java">
 +
final Display display = new Display();
 +
final Runnable bgRunnable = new Runnable() {
 +
  public void run() {
 +
    RWT.getUISession( display ).exec( new Runnable() {
 +
      public void run() {
 +
        Object someValue = RWT.getUISession().getAttribute( "myAtt" );
 +
        System.out.println( someValue );
 +
        // access the HTTP session
 +
        RWT.getUISession().getHttpSession();
 +
        // access session singleton
 +
        SingletonUtil.getSessionInstance( MyClass.class );
 +
      }
 +
    } );
 +
  }
 +
};
 +
Shell shell = new Shell( display );
 +
Button button = new Button( shell, SWT.PUSH );
 +
button.setText( "Start Background Thread" );
 +
button.addSelectionListener( new SelectionAdapter() {
 +
  public void widgetSelected( final SelectionEvent evt ) {
 +
    RWT.getUISession().setAttribute( "myAtt", "someValue" );
 +
    Thread thread = new Thread( bgRunnable );
 +
    thread.setDaemon( true );
 +
    thread.start();
 +
  }
 +
} );
 +
</source>
  
Your code tries to access session-information but isn't run from the UI thread (the only thread that has a session context associated by default).
+
=== How to integrate the Eclipse Help System in a RAP application?  ===
  
The soltion is to wrap your code in a Runnable and have it executed by <code>UICallBack#runNonUIThreadWithFakeContext()</code>.
+
'''Applies to RAP 1.1 and 1.2'''
  
Please also see this thread: [http://dev.eclipse.org/newslists/news.eclipse.technology.rap/msg01311.html]
+
An example of how to integrate the Eclipse Help webapplication <code>org.eclipse.help.webapp</code> into a RAP application is provided here [[Image:Org.eclipse.rap.help.integration.zip]].
  
=== How to integrate the Eclipse Help System in a RAP application?  ===
+
After importing the example project, the following plugins have to be added, since they are not included in the RAP target:
'''Applies to RAP 1.1 and 1.2'''
+
  
An example of how to integrate the Eclipse Help webapplication <code>org.eclipse.help.webapp</code> into a RAP application is provided here [[Image:Org.eclipse.rap.help.integration.zip]].  
+
*javax.servlet.jsp
 +
*org.apache.commons.el
 +
*org.apache.commons.logging
 +
*org.apache.jasper
 +
*org.apache.lucene.analysis
 +
*org.apache.lucene
 +
*org.eclipse.equinox.jsp.jasper.registry
 +
*org.eclipse.equinox.jsp.jasper
 +
*org.eclipse.help.appserver
 +
*org.eclipse.help.base
 +
*org.eclipse.help.webapp
 +
*org.eclipse.help
  
After importing the example project, the following plugins have to be added, since they are not included in the RAP target:
+
The plug-ins mentioned above should be taken from a "suitable" Eclipse installation. Currently (RAP 1.2) this is version 3.4.  
*javax.servlet.jsp
+
*org.apache.commons.el
+
*org.apache.jasper
+
*org.apache.lucene.analysis
+
*org.apache.lucene
+
*org.eclipse.equinox.jsp.jasper.registry
+
*org.eclipse.equinox.jsp.jasper
+
*org.eclipse.help.appserver
+
*org.eclipse.help.base
+
*org.eclipse.help.webapp
+
*org.eclipse.help
+
The plug-ins mentioned above should be taken from a "suitable" Eclipse installation. Currently (RAP 1.2) this is version 3.4.
+
  
Please note that the included launch configuration must be used. It contains the <code>-Dorg.eclipse.equinox.http.jetty.other.info=org.eclipse.help</code> VM argument.
+
Please note that the included launch configuration must be used. It contains the <code>-Dorg.eclipse.equinox.http.jetty.other.info=org.eclipse.help</code> VM argument. Furthermore, the launch config assumes that the "RAP plug-ins" (...workbench etc.) are in your workspace. The target only contains the "Base plug-ins" (...equinox, ..core.runtime). In case your setup differs, you will need to adjust it.  
Furthermore, the launch config assumes that the "RAP plug-ins" (...workbench etc.) are in your workspace. The target only contains the "Base plug-ins" (...equinox, ..core.runtime). In case your setup differs, you will need to adjust it.
+
  
'''Applies to RAP 1.3'''
+
'''Applies to RAP 1.3'''  
  
As RAP 1.3 (starting from [http://eclipse.org/rap/noteworthy/news_13M2.php#Workbench M2]) delivers the infrastructure for the Help system, some of the facts above need to be adjusted. RAP itself only provides the core infrastructure for (like org.eclipse.help) but without the concrete implementation. This means you still need the plug-ins mentioned above (except org.eclipse.help).
+
As RAP 1.3 (starting from [http://eclipse.org/rap/noteworthy/news_13M2.php#Workbench M2]) delivers the infrastructure for the Help system, some of the facts above need to be adjusted. RAP itself only provides the core infrastructure for (like org.eclipse.help) but without the concrete implementation. This means you still need the plug-ins mentioned above (except org.eclipse.help). In case you don't need a sophisticed help solution (no dynamic help, no interaction with cheatsheets, no context help) the solution above is still sufficient. If you need better help integration (which is normally handled by org.eclipse.help.ui) you need to provide your own help ui contributions. This can be achieved by extending the [http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/extension-points/org_eclipse_ui_helpSupport.html org.eclipse.ui.helpSupport] extension point.  
In case you don't need a sophisticed help solution (no dynamic help, no interaction with cheatsheets, no context help) the solution above is still sufficient.
+
If you need better help integration (which is normally handled by org.eclipse.help.ui) you need to provide your own help ui contributions. This can be achieved by extending the [http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/extension-points/org_eclipse_ui_helpSupport.html org.eclipse.ui.helpSupport] extension point.
+
  
An example of contributing a new helpSupport extension can be found in in this project: [[Image:RapHelpSupport.zip]].
+
An example of contributing a new helpSupport extension can be found in in this project: [[Image:RapHelpSupport.zip]].  
  
=== How to integrate BIRT? ===
+
<br> '''The help system customization'''
Please read [http://wiki.eclipse.org/RAP/BIRT_Integration Integrating BIRT into RAP applications] to get started.
+
  
=== How to use expression-based activities for role-based access control? ===
+
The help system customization (like help window title or home page) in a RAP application is simply done by using the org.eclipse.core.runtime.products extension. Even if this extension is not fully supported by RAP, it is necessary to customize the help system. So:
  
The expression-based activities can be used to restrict the access to UI elements based on user roles.
+
1) add the following in the plugin.xml: <!-- Used only for customization of the help system --> <!-- "eclipse.product=my.plugin.help_id" must be set in config.ini -->
The following example defines an action <code>demo.xyz.Action1</code>.  
+
 
 +
&lt;extension
 +
    point="org.eclipse.core.runtime.products"
 +
    id="help_id"&gt;
 +
  &lt;product
 +
      application="dummy"
 +
      description="Integration With Eclipse Help System"
 +
      name="%application.name"&gt;
 +
    &lt;property
 +
        name="preferenceCustomization"
 +
        value="product_customization.ini"&gt;
 +
    &lt;/property&gt;
 +
  &lt;/product&gt;
 +
&lt;/extension&gt;
 +
 
 +
2) create the product_customization.ini file in the root folder of my.plugin and add the needed configuration as specified by the product customization guide. For example it could contain:
 +
 
 +
# Toc ordering.  Ordered list of help TOC's (books) as they would appear
 +
# on the bookshelf. All the other TOCS will be following these books.
 +
org.eclipse.help/baseTOCS=\
 +
  /my.plugin.ui.doc/toc.xml
 +
# help system home page
 +
org.eclipse.help.base/help_home=/my.plugin/doc/help_home.html
 +
 
 +
3) add the following VM argument to the launch configuration:
 +
 
 +
-Declipse.product=my.plugin.help_id
 +
 
 +
'''Building and deploying as web application'''
 +
 
 +
In order to make the help system available when building and deploying the RAP application as a web application, the following setting must be added to the WEB-INF/web.xml:
 +
 
 +
&lt;init-param&gt;
 +
  &lt;param-name&gt;other.info&lt;/param-name&gt;
 +
  &lt;param-value&gt;org.eclipse.help&lt;/param-value&gt;
 +
&lt;/init-param&gt;
 +
 
 +
(analogue to setting the -Dorg.eclipse.equinox.http.jetty.other.info=org.eclipse.help in the launch configuration)
 +
 
 +
In order to add the help system customization when building and deploying the RAP application as a web application, the following setting must be added to the config.ini: eclipse.product=my.plugin.help_id (analogue to the -Declipse.product=my.plugin.help_id in the launch configuration.
 +
 
 +
To use the help system in Websphere, you have to add another servlet mapping for .jsp files, see https://stackoverflow.com/questions/20972242/eclipse-help-of-rap-2-0-application-does-not-work-with-websphere-8-5-5-0.
 +
 
 +
=== How to integrate BIRT?  ===
 +
 
 +
Please read [[RAP/BIRT Integration|Integrating BIRT into RAP applications]] to get started.
 +
 
 +
=== How to use expression-based activities for role-based access control?  ===
 +
 
 +
The expression-based activities can be used to restrict the access to UI elements based on user roles. The following example defines an action <code>demo.xyz.Action1</code>.  
  
 
<source lang="xml">
 
<source lang="xml">
Line 151: Line 219:
 
     </actionSet>
 
     </actionSet>
 
   </extension>
 
   </extension>
</source>
+
</source>  
  
Let's suppose that this action should be available only to users with role <code>admin</code>.  
+
Let's suppose that this action should be available only to users with role <code>admin</code>. We will create an expression-based activity <code>demo.Activity1</code>, which will be enabled when the variable <code>demo.roles</code> contains the string <code>admin</code>. Trough the associated pattern binding, the activity will control the availability of the <code>demo.xyz.Action1</code> action and the corresponding menu item.  
We will create an expression-based activity <code>demo.Activity1</code>, which will be enabled when the variable <code>demo.roles</code> contains the string <code>admin</code>.
+
Trough the associated pattern binding, the activity will control the availability of the <code>demo.xyz.Action1</code> action and the corresponding menu item.
+
  
 
<source lang="xml">
 
<source lang="xml">
Line 173: Line 239:
 
     </activityPatternBinding>
 
     </activityPatternBinding>
 
   </extension>
 
   </extension>
</source>
+
</source>  
  
 
<source lang="xml">
 
<source lang="xml">
Line 182: Line 248:
 
     </sourceProvider>
 
     </sourceProvider>
 
   </extension>
 
   </extension>
</source>
+
</source>  
  
The value of the <code>demo.roles</code> variable is provided by a subclass of <code>AbstractSourceProvider</code>. A dummy implementation is shown below.  
+
The value of the <code>demo.roles</code> variable is provided by a subclass of <code>AbstractSourceProvider</code>. A dummy implementation is shown below. The actual implementation should obtain the current user roles from a real authentication provider service.  
The actual implementation should obtain the current user roles from a real authentication provider service.
+
  
 
<source lang="java">
 
<source lang="java">
Line 217: Line 282:
 
   }
 
   }
 
  }
 
  }
</source>
+
</source> A small application, which illustrates the outlined approach, can be found here: [[Image:Activities-roles-demo.zip|Demo application]]  
A small application, which illustrates the outlined approach, can be found here: [[Image:Activities-roles-demo.zip|Demo application]]
+
  
 
=== What is a Session Singleton and how can I implement one?  ===
 
=== What is a Session Singleton and how can I implement one?  ===
  
A "session singleton" is an RWT-specific singleton, which provides access to a unique instance with session scope. This means that in the context of one user session <code>getInstance(Class)</code> will always return the same object, but for different user sessions the returned instances will be different. To archive it's "magic", session singletons should call SessionSingletonBase#getInstance(), which takes care of the proper scoping of the singleton instances. The following code snippet illustrates this pattern:  
+
A "session singleton" is an RWT-specific singleton, which provides access to a unique instance with session scope. This means that in the context of one user session <code>getInstance()</code> will always return the same object, but for different user sessions the returned instances will be different. RWT provides a utility to easily create session singletons, called ''SingletonUtil''. Here's an example of a session singleton:
  
 
<source lang="java">
 
<source lang="java">
Line 231: Line 295:
  
 
   public static MySessionSingleton getInstance() {
 
   public static MySessionSingleton getInstance() {
     return ( MySessionSingleton )SessionSingletonBase.getInstance( MySessionSingleton.class );
+
     return SingletonUtil.getSessionInstance( MySessionSingleton.class );
 
   }
 
   }
 
   // add other methods ...
 
   // add other methods ...
 
}
 
}
</source>  
+
</source>
  
Accessing session singletons is possible only from threads which are associated with a session. The <code>UIThread</code> always has a such an association, therefore any code running in this thread can freely access session singletons. Any non-UI (i.e. "background") thread, trying to access session singletons, will fail and a <code>java.lang.IllegalStateException</code> will be thrown. The solution is to temporarily associate the thread with a session. Use <code>UICallBack#runNonUIThreadWithFakeContext</code>, to fakes a "request context" that allows the runnable code to access those singletons.  
+
Note that session singletons can only be directly accessed from the UIThread or with a ''fake context''. When the  second parameter "UISession" (introduced in RAP 2.2) is used the thread/context no longer matters.
  
The following code snippet throws an exception because the <code>Runnable</code> is executed in a background thread, with no access to the session singleton.  
+
More on session scope and session singletons can be found in the [http://eclipse.org/rap/developers-guide/devguide.php?topic=singletons.html section on scopes and data stores in RWT] in the RAP Developer's Guide.
  
<source lang="java">
+
'''Note:''' For RAP < 2.0 use <code>SessionSingletonBase.getInstance(Class)</code>
// INCORRECT
+
// will throw IllegalStateException: No context available outside of the request service lifecycle.
+
Runnable runnable = new Runnable() {
+
  public void run() {
+
    MySessionSingleton sessionSingleton = MySessionSingleton.getInstance();
+
    // do something with the session singleton
+
  }
+
};
+
new Thread( runnable ).start();
+
</source>
+
 
+
Here the <code>Runnable</code> is executed via <code>UICallBack.runNonUIThreadWithFakeContext</code> which will provide the context, required for accessing the session singleton.
+
 
+
<source lang="java">
+
// CORRECT
+
final Display display = Display.getCurrent();
+
final Runnable runnable = new Runnable() {
+
  public void run() {
+
    UICallBack.runNonUIThreadWithFakeContext( display, new Runnable() {
+
      public void run() {
+
        MySessionSingleton sessionSingleton = MySessionSingleton.getInstance();
+
        // do something with the session singleton
+
      }
+
    } );
+
  }
+
};
+
new Thread( runnable ).start();
+
</source>  
+
  
 
=== How to add a Welcome page to a RAP application?  ===
 
=== How to add a Welcome page to a RAP application?  ===
Line 312: Line 348:
 
</source>  
 
</source>  
  
Note: the redirect servlet is needed here since registering a welcome page resource directly for the "/" alias does not work: a trailing slash would be appended to the resource URL.
+
Note: the redirect servlet is needed here since registering a welcome page resource directly for the "/" alias does not work: a trailing slash would be appended to the resource URL.  
  
 
=== I do ... in my bundle activator. Does not work as expected in RAP.  ===
 
=== I do ... in my bundle activator. Does not work as expected in RAP.  ===
Line 320: Line 356:
 
Since you have a multi user environment in RAP (=&gt; multiple Sessions at a time) but only one application wide equinox instance running which is shared between those sessions (application scope) things getting a little bit more complicated. Each session has its own workbench instance since the state of the UIs differ between sessions. So in principle you can easily access the core from the UI, but the other way round isn't possible without additional code.  
 
Since you have a multi user environment in RAP (=&gt; multiple Sessions at a time) but only one application wide equinox instance running which is shared between those sessions (application scope) things getting a little bit more complicated. Each session has its own workbench instance since the state of the UIs differ between sessions. So in principle you can easily access the core from the UI, but the other way round isn't possible without additional code.  
  
So what's happening in your code is that you try to access your GUI layer (session scope) from the bundle activator (application scope), which isn't possible as was just explained. Most of the time it is easy to move the code from the activator to the <code>IEntryPoint#createUI</code> or <code>IStartup#earlyStartup</code>. Both methods are executed in session scope context, so there won't be any <code>IllegalStateExceptions</code> thrown.
+
So what's happening in your code is that you try to access your GUI layer (session scope) from the bundle activator (application scope), which isn't possible as was just explained. Most of the time it is easy to move the code from the activator to the <code>EntryPoint#createUI</code> or <code>IStartup#earlyStartup</code>. Both methods are executed in session scope context, so there won't be any <code>IllegalStateExceptions</code> thrown.
  
=== How to provide download link? ===
+
=== How to add a link to an external URL? ===
  
To provide download functionality in a RAP (or pure RWT) application, you need two components:
+
Since RAP 1.5, you can use simple HTML markup in a couple of widgets, including Label:
  
* a custom service handler (implementation of <code>IServiceHandler</code>) to handle the download request outside the standard RWT request lifecycle;
+
<source lang="java">
* an instance of the <code>Browser</code> containing an HTML ''Anchor'' tag, on which the user shall click to start the download. 
+
Label label = new Label( page, SWT.NONE );
 +
label.setData( RWT.MARKUP_ENABLED, Boolean.TRUE );
 +
label.setText( "<a href=\"http://xkcd.com/\" target=\"_blank\">Plain Link</a>" );
 +
</source>
  
==== The service handler ====
+
Alternatively, you can use a browser widget that contains the link:
  
Below is an example of a very basic downloads service handler. The request has a parameter "filename" which indicates which content (either static or dynamically generated) should be sent to the client. The ''MyDataStore'' will known how to find the required content. In our example the content is retrieved as <code>byte[]</code> but <code>InputStream</code>s and <code>Reader</code>s can be easily accommodated as well.
+
<source lang="java">
 +
Browser browser = new Browser( parent, SWT.NONE );
 +
browser.setText( "<a href=\"http://xkcd.com/\" target=\"_blank\">Plain Link</a>" );
 +
</source>
 +
 
 +
A third option is to open an external browser window programmatically. However, this will be prevented by popup blockers.
  
 
<source lang="java">
 
<source lang="java">
public class DownloadServiceHandler implements IServiceHandler {
+
Button button = new Button( page, SWT.PUSH );
 +
button.setText( "External Browser" );
 +
button.addSelectionListener( new SelectionAdapter() {
 +
  @Override
 +
  public void widgetSelected( SelectionEvent e ) {
 +
    int browserStyle = ExternalBrowser.LOCATION_BAR | ExternalBrowser.NAVIGATION_BAR;
 +
    ExternalBrowser.open( "google", "http://xkcd.com/", browserStyle );
 +
  }
 +
} );
 +
</source>
  
   public void service() throws IOException, ServletException {
+
In RAP 2.0 or later, use the URLLauncher instead of ExternalBrowser.
 +
 
 +
=== How to provide a download link?  ===
 +
 
 +
To provide download functionality in a RAP (or pure RWT) application, you need two components:
 +
 
 +
* a download link (see above)
 +
* a service handler (implementation of <code>ServiceHandler</code>) to provide the download content
 +
 
 +
Below is an example of a very basic downloads service handler. The request has a parameter "filename" which indicates which content (either static or dynamically generated) should be sent to the client. The ''MyDataStore'' will known how to find the required content. In our example the content is retrieved as <code>byte[]</code> but <code>InputStream</code>s and <code>Reader</code>s can be easily accommodated as well.
 +
 
 +
<source lang="java">
 +
public class DownloadServiceHandler implements ServiceHandler {
 +
 
 +
   public void service( HttpServletRequest request, HttpServletResponse response )
 +
    throws IOException, ServletException
 +
  {
 
     // Which file to download?
 
     // Which file to download?
     String filename = RWT.getRequest().getParameter( "filename" );
+
     String fileName = request.getParameter( "filename" );
     // Get the file content  
+
     // Get the file content
     byte[] download = MyDataStore.getByteArrayData( filename );
+
     byte[] download = MyDataStore.getByteArrayData( fileName );
     // Send the file in the response.
+
     // Send the file in the response  
    HttpServletResponse response = RWT.getResponse();
+
 
     response.setContentType( "application/octet-stream" );
 
     response.setContentType( "application/octet-stream" );
 
     response.setContentLength( download.length );
 
     response.setContentLength( download.length );
     response.setHeader( "Content-Disposition", "attachment; filename=\""
+
     String contentDisposition = "attachment; filename=\"" + fileName + "\"";
                                              + filename
+
     response.setHeader( "Content-Disposition", contentDisposition );
                                              + "\"" );
+
     response.getOutputStream().write( download );
     try {
+
      response.getOutputStream().write( download );
+
     } catch( IOException e1 ) {
+
      e1.printStackTrace();
+
    }
+
 
   }
 
   }
}
+
}
</source>
+
</source>  
  
The service handler has to be registered:
+
The service handler has to be registered:  
  
 
<source lang="java">
 
<source lang="java">
RWT.getServiceManager()
+
ServiceManager manager = RWT.getServiceManager();
      .registerServiceHandler( "downloadServiceHandler",
+
ServiceHandler handler = new DownloadServiceHandler();
                              new DownloadServiceHandler() );
+
manager.registerServiceHandler( "downloadServiceHandler", handler );
 
</source>
 
</source>
  
==== The <code>Browser</code> with the anchor tag ====
+
The download link has to point to a URL created as follows:
 
+
To start the actual download, we shall provide the user with a hyperlink to click on. It can be attached either to text or image, depending of the UI design requirements. An appropriate CSS style definition can be used to blend the hyperlink with the rest of the UI.
+
  
 
<source lang="java">
 
<source lang="java">
private void createDownloadLink( Composite parent ) {
 
  Browser browser = new Browser( parent, SWT.NONE );
 
  browser.setText( createDownloadHtml( "foo.txt", "Download file" ) );
 
}
 
 
private String createDownloadHtml( String filename, String text ) {
 
  StringBuilder html = new StringBuilder();
 
  html.append( "<a href=\"" );
 
  html.append( createDownloadUrl( filename ) );
 
  html.append( "\">" );
 
  html.append( text );
 
  html.append( "</a>" );
 
  return html.toString();
 
}
 
 
 
private String createDownloadUrl( String filename ) {
 
private String createDownloadUrl( String filename ) {
 
   StringBuilder url = new StringBuilder();
 
   StringBuilder url = new StringBuilder();
   url.append( RWT.getRequest().getContextPath() );
+
   url.append( RWT.getServiceManager().getServiceHandlerUrl( "downloadServiceHandler" ) );
  url.append( RWT.getRequest().getServletPath() );
+
   url.append( '&' ).append( "filename" ).append( '=' ).append( filename );
  url.append( "?" );
+
   return url.toString();
   url.append( IServiceHandler.REQUEST_PARAM );
+
  url.append( "=downloadServiceHandler" );
+
  url.append( "&filename=" );
+
  url.append( filename );
+
   String encodedURL = RWT.getResponse().encodeURL( url.toString() );
+
  return encodedURL;
+
 
}
 
}
 
</source>
 
</source>
  
=== How to switch locale/language on user action? ===
+
=== How to switch locale/language on user action? ===
  
Switching the locale in a running session isn't supported out of the box. So currently you'll have to restart the session to be able to switch the locale.
+
Switching the locale in a running session isn't supported out of the box. So currently you'll have to restart the session to be able to switch the locale.  
  
To restart the session, we will send some Javascript code, which changes the <code>parent.window.location.href</code> DOM attribute of the current page. The new <code>parent.window.location.href</code> value is the URL of the current page plus a new parameter which contains the selected locale code.
+
To restart the session, we will send some Javascript code, which changes the <code>parent.window.location.href</code> DOM attribute of the current page. The new <code>parent.window.location.href</code> value is the URL of the current page plus a new parameter which contains the selected locale code.  
  
==== Restarting the session and passing the new locale as a parameter ====
+
'''Restarting the session and passing the new locale as a parameter'''
  
The <code>changeLocale</code> method is invoked when the user requests a language change (e.g. from a selection listener attached to a button). In order to add the page-reloading Javascript code to the HTTP response, we register a life-cycle <code>PhaseListener</code>:
+
The <code>changeLocale</code> method is invoked when the user requests a language change (e.g. from a selection listener attached to a button). In order to add the page-reloading Javascript code to the HTTP response, we register a life-cycle <code>PhaseListener</code>:  
  
 
<source lang="java">
 
<source lang="java">
Line 413: Line 453:
 
void changeLocale( String locale ) {
 
void changeLocale( String locale ) {
 
   final String url = createUrl( locale  );
 
   final String url = createUrl( locale  );
   final Display display = Display.getCurrent();
+
   // Uses a non-public API, see http://bugs.eclipse.org/342995
  RWT.getLifeCycle().addPhaseListener( new PhaseListener() {
+
  JSExecutor.executeJS( "parent.window.location.href=\"" + url + "\";" );
 
+
    private static final long serialVersionUID = 1L;
+
 
+
    public void afterPhase( PhaseEvent event ) {
+
      if( display == Display.getCurrent() ) {
+
        try {
+
          // Uses a non-public API, but currently this is the only solution
+
          HtmlResponseWriter writer
+
            = ContextProvider.getStateInfo().getResponseWriter();
+
          writer.write( "parent.window.location.href=\"" + url + "\";" );
+
        } catch( IOException e ) {
+
          // ignore
+
        }
+
        RWT.getLifeCycle().removePhaseListener( this );
+
      }
+
    }
+
 
+
    public PhaseId getPhaseId() {
+
      return PhaseId.RENDER;
+
    }
+
 
+
    // ...
+
 
+
  } );
+
 
}
 
}
  
Line 452: Line 468:
 
</source>
 
</source>
  
==== Getting the locale from the URL parameter ====
+
'''Getting the locale from the URL parameter'''
  
Before creating the UI, set the locale to the value retrieved from the URL parameter:
+
Before creating the UI, set the locale to the value retrieved from the URL parameter:  
  
 
<source lang="java">
 
<source lang="java">
Line 469: Line 485:
 
</source>
 
</source>
  
=== How to display dynamically created images? ===
+
=== How to display dynamically created images? ===
  
* Create an instance of <code>Browser</code> widget to place the image in the UI
+
* Create an instance of <code>Browser</code> widget to place the image in the UI  
* Create an image, e.g. using <code>java.awt.Graphics2D</code>
+
* Create an image, e.g. using <code>java.awt.Graphics2D</code>  
 
* Use a custom service handler to deliver the image.
 
* Use a custom service handler to deliver the image.
  
==== Create a <code>Browser</code> widget to render the image ====
+
'''Create a Browser widget to render the image'''
  
 
To place the image in the application UI, we use a <code>Browser</code> widget with a single HTML <code>img</code> tag. The URL in the <code>src</code> attribute contains a ''key'', used to identify the generated image.  
 
To place the image in the application UI, we use a <code>Browser</code> widget with a single HTML <code>img</code> tag. The URL in the <code>src</code> attribute contains a ''key'', used to identify the generated image.  
Line 492: Line 508:
 
     // create the image
 
     // create the image
 
     BufferedImage image = createImage();
 
     BufferedImage image = createImage();
     // store the image in the SessionStore for the service handler
+
     // store the image in the UISession for the service handler
     RWT.getSessionStore().setAttribute( IMAGE_KEY, image );
+
     RWT.getUISession().setAttribute( IMAGE_KEY, image );
 
     // create the HTML with a single <img> tag.
 
     // create the HTML with a single <img> tag.
 
     browser.setText( createHtml( IMAGE_KEY ) );
 
     browser.setText( createHtml( IMAGE_KEY ) );
Line 507: Line 523:
 
   }
 
   }
  
   private String createImageUrl( String string ) {
+
   private String createImageUrl( String key ) {
 
     StringBuffer url = new StringBuffer();
 
     StringBuffer url = new StringBuffer();
     url.append( RWT.getRequest().getContextPath() );
+
     url.append( RWT.getServiceManager().getServiceHandlerUrl( SERVICE_HANDLER ) );
    url.append( RWT.getRequest().getServletPath() );
+
    url.append( "?" );
+
    url.append( IServiceHandler.REQUEST_PARAM );
+
    url.append( "=" );
+
    url.append( SERVICE_HANDLER );
+
 
     url.append( "&imageId=" );
 
     url.append( "&imageId=" );
     url.append( string );
+
     url.append( key );
 
     url.append( "&nocache=" );
 
     url.append( "&nocache=" );
 
     url.append( System.currentTimeMillis() );
 
     url.append( System.currentTimeMillis() );
    String encodedURL = RWT.getResponse().encodeURL( url.toString() );
+
     return url;
     return encodedURL;
+
 
   }
 
   }
 
}
 
}
  
</source>
+
</source>  
  
==== Create the image ====
+
'''Create the image'''
  
Use <code>java.awt.Graphics2D</code> as a canvas on which to draw the image. The server, where the application will be deployed, most probably has no graphics UI system installed. Therefore we need to run AWT in "headless mode" by setting the system property <code>java.awt.headless</code> to ''true'':
+
Use <code>java.awt.Graphics2D</code> as a canvas on which to draw the image. The server, where the application will be deployed, most probably has no graphics UI system installed. Therefore we need to run AWT in "headless mode" by setting the system property <code>java.awt.headless</code> to <code>true</code>.
  
<code>
+
In the example below, the produced <code>BufferedImage</code> is placed in the <code>UISession</code> and the ''key'' is used to generate the new <code>&lt;img&gt;</code> tag for the <code>Browser</code>.  
  -Djava.awt.headless=true
+
</code>
+
 
+
In the example below, the produced <code>BufferedImage</code> is placed in the <code>SessionStore</code> and the ''key'' is used to generate the new <code><img></code> tag for the <code>Browser</code>.  
+
  
 
<source lang="java">
 
<source lang="java">
  private BufferedImage createImage() {
+
private BufferedImage createImage() {
    BufferedImage image = new BufferedImage( 200, 200, BufferedImage.TYPE_INT_ARGB );
+
  BufferedImage image = new BufferedImage( 200, 200, BufferedImage.TYPE_INT_ARGB );
    Graphics2D gr2d = image.createGraphics();
+
  Graphics2D gr2d = image.createGraphics();
    // draw the image
+
  // draw the image
    gr2d.setColor( new Color( 0, 0, 255 ) );
+
  gr2d.setColor( new Color( 0, 0, 255 ) );
    gr2d.drawRect( 0, 0, widht - 1, height - 1 );
+
  gr2d.drawRect( 0, 0, widht - 1, height - 1 );
    ...
+
  ...
    return image;
+
  return image;
  }
+
}
</source>
+
</source>  
  
==== Service Handler ====
+
'''Service Handler'''
  
The service handler has to be registered with <code>RWT.getServiceManager().registerServiceHandler()</code> - see the first code snippet above.
+
The service handler has to be registered with <code>RWT.getServiceManager().registerServiceHandler()</code> - see the first code snippet above. The handler receives the image ''key'' as a request parameter, and uses it to retrieve the image from the <code>UISession</code>.  
The handler receives the image ''key'' as a request parameter, and uses it to retrieve the image from the <code>SessionStore</code>.
+
  
 
<source lang="java">
 
<source lang="java">
    public class ImageServiceHandler implements IServiceHandler {
+
public class ImageServiceHandler implements ServiceHandler {
 
+
  public void service( HttpServletRequest request, HttpServletResponse response )
      public void service() throws IOException, ServletException {
+
    throws IOException, ServletException
        String id = RWT.getRequest().getParameter( "key" );
+
  {
        BufferedImage image = ( BufferedImage )RWT.getSessionStore()
+
    String id = request.getParameter( "imageId" );
          .getAttribute( id );
+
    BufferedImage image = ( BufferedImage )RWT.getUISession().getAttribute( id );
        HttpServletResponse response = RWT.getResponse();
+
    response.setContentType( "image/png" );
        response.setContentType( "image/png" );
+
    ServletOutputStream out = response.getOutputStream();
        try {
+
    ImageIO.write( image, "png", out );
          ServletOutputStream out = response.getOutputStream();
+
  }
          ImageIO.write( image, "png", out );
+
}
        } catch( IOException e ) {
+
          e.printStackTrace();
+
        }
+
      }
+
    }
+
 
</source>
 
</source>
  
=== How to deliver ''session-scoped'' resources ===
+
=== How to deliver ''session-scoped'' resources ===
  
Often a RWT application UI includes ''session specific'' resources (e.g. images) - either because they are dynamically created or specific credentials are required to access them. This use case can be handled by a custom ''service handler'' and a <code>Browser</code> widget (or a custom widget). Please see the FAQ article [[#How to display dynamically created images?]] for the implementation details.
+
Often a RWT application UI includes ''session specific'' resources (e.g. images) - either because they are dynamically created or specific credentials are required to access them. This use case can be handled by a custom ''service handler'' and a <code>Browser</code> widget (or a custom widget). Please see the FAQ article [[#How_to_display_dynamically_created_images.3F]] for the implementation details.  
  
=== How to specify context path when using the RAP launcher? ===
+
<br>  
 
+
For the embedded Jetty servlet container, used by the RAP launcher, the context path can be specified as a VM parameter in the launch configuration:
+
 
+
<code>
+
  -Dorg.eclipse.equinox.http.jetty.context.path=<context path>
+
</code>
+
 
+
In the launch configuration dialog, you should also change the '''Servlet name''' if the '''Open application in ...''' is checked.
+
 
+
Here is a complete example:
+
 
+
* RAP launcher configuiration
+
<code>
+
  VM argument: -Dorg.eclipse.equinox.http.jetty.context.path=/mycontext
+
  Servlet name: mycontext/rap
+
  Entry point: myentry
+
</code>  
+
  
* Application URL
+
=== How to access a RAP application without specifying a servlet name? ===
<code> 
+
  <nowiki>http://<host>:<port>/mycontext/rap?startup=myentry</nowiki>
+
</code>
+
  
=== How to access a RAP application without specifying a servlet name? ===
+
Let's suppose that we want to simplify the URL of our RAP application and be able to access it with <code><nowiki>http://www.example.org/webapp</nowiki></code> instead of <code><nowiki>http://www.example.org/webapp/servlet_path</nowiki></code>.
  
Let's suppose that we want to simplify the URL of our RAP application and be able to access it with <code><nowiki>http://www.example.org/webapp</nowiki></code> instead of <code><nowiki>http://www.example.org/webapp/servlet_name</nowiki></code>.
+
The recommended solution is to use a redirection servlet:  
  
The recommended solution is to use a redirection servlet:
+
*Write a servlet which redirects requests for the web application root to your application servlet path:
*Write a servlet which redirects requests for the web application root to the servlet name defined in the <code>org.eclipse.rap.branding</code> extension:
+
  
 
<source lang="java">
 
<source lang="java">
Line 629: Line 608:
 
   {
 
   {
 
     if( request.getPathInfo().equals( "/" ) ) {
 
     if( request.getPathInfo().equals( "/" ) ) {
       response.sendRedirect( response.encodeRedirectURL( "servlet_name" ) );
+
       response.sendRedirect( response.encodeRedirectURL( "servlet_path" ) );
 
     } else {
 
     } else {
 
       response.sendError( HttpServletResponse.SC_NOT_FOUND );
 
       response.sendError( HttpServletResponse.SC_NOT_FOUND );
Line 655: Line 634:
 
Assuming you are running your RAP application with Jetty, it is possible to setup the [http://docs.codehaus.org/display/JETTY/How+to+Configure+Security+with+Embedded+Jetty basic authentication mechanism] that will cause the browser to ask the user for a username and password:  
 
Assuming you are running your RAP application with Jetty, it is possible to setup the [http://docs.codehaus.org/display/JETTY/How+to+Configure+Security+with+Embedded+Jetty basic authentication mechanism] that will cause the browser to ask the user for a username and password:  
  
*First create a class that extends JettyCustomizer. This will allow us to customize the Jetty context to setup the basic authentication mechanism. Here is a sample class:
+
*First create a class that extends <code>JettyCustomizer</code>. This will allow you to customize the Jetty context to setup the basic authentication mechanism. Here is a sample class:
 +
 
 
<source lang="java">
 
<source lang="java">
 
public class BasicAuthCustomizer extends JettyCustomizer {  
 
public class BasicAuthCustomizer extends JettyCustomizer {  
  
   public Object customizeContext(Object o, Dictionary dictionary) {
+
   public Object customizeContext( Object o, Dictionary dictionary ) {
     final Constraint constraint = new Constraint();
+
     Constraint constraint = new Constraint();
     constraint.setName(Constraint.__BASIC_AUTH);
+
     constraint.setName( Constraint.__BASIC_AUTH );
     constraint.setRoles(new String[]{ "admin" });
+
     constraint.setRoles( new String[]{ "admin" } );
     constraint.setAuthenticate(true);
+
     constraint.setAuthenticate( true );
 
    
 
    
     final ConstraintMapping cm = new ConstraintMapping();
+
     ConstraintMapping mapping = new ConstraintMapping();
     cm.setConstraint(constraint);
+
     mapping.setConstraint( constraint );
     cm.setPathSpec("/*");
+
     mapping.setPathSpec( "/*" );
  
     final HashUserRealm realm = new HashUserRealm("MyRealm");
+
     HashUserRealm realm = new HashUserRealm( "MyRealm" );
 
     .. // fill username, password and roles if needed
 
     .. // fill username, password and roles if needed
  
     final SecurityHandler sh = new SecurityHandler();
+
     SecurityHandler securityHandler = new SecurityHandler();
     sh.setUserRealm(realm);
+
     securityHandler.setUserRealm( realm );
     sh.setConstraintMappings(new ConstraintMapping[]{cm});
+
     securityHandler.setConstraintMappings( new ConstraintMapping[]{ mapping } );
  
     final Context c = (Context) o;
+
     Context context = ( Context )o;
     c.setSecurityHandler(sh);
+
     context.setSecurityHandler( securityHandler );
  
 
     return o;
 
     return o;
Line 684: Line 664:
 
}
 
}
 
</source>  
 
</source>  
 +
 
*Specify the fully qualified name of this class in your start script. For example:
 
*Specify the fully qualified name of this class in your start script. For example:
 +
<pre>-Dorg.eclipse.equinox.http.jetty.customizer.class=com.mycompany.console.jetty.BasicAuthCustomizer
 +
</pre>
 +
*In order for this class to be loaded correctly by the Eclipse code that customizes Jetty, you will need to put this class into a fragment that attaches to the <code>org.eclipse.equinox.http.jetty</code> bundle.
  
<pre>
+
=== How to register a file/folder as resource?  ===
-Dorg.eclipse.equinox.http.jetty.customizer.class=com.mycompany.console.jetty.BasicAuthCustomizer
+
</pre>
+
  
*In order for this class to be loaded correctly by the Eclipse code that customizes Jetty, you will need to put this class into a fragment that attaches to the "org.eclipse.equinox.http.jetty" bundle.
+
Arbitrary resources can be registered via the extension point "org.eclipse.equinox.http.registry.resources".  
  
=== How to register a file/folder as resource? ===
+
Example: <source lang="xml">
 
+
Arbitrary resources can be registered via the extension point "org.eclipse.equinox.http.registry.resources".
+
 
+
Example:
+
<source lang=xml>
+
 
<extension
 
<extension
 
       point="org.eclipse.equinox.http.registry.resources">
 
       point="org.eclipse.equinox.http.registry.resources">
Line 705: Line 682:
 
   </resource>
 
   </resource>
 
</extension>
 
</extension>
</source>
+
</source>  
  
The properties are used the following way:
+
The properties are used the following way:  
* base-name: The path which is to be mapped (project folder is root).
+
* alias: Path the base-name is mapped to on the server.
+
  
In the example above the folder ".../<project_name>/resources" is mapped to the server root <code><nowiki>http://<url_to_rap_server>/</nowiki></code>.
+
*base-name: The path which is to be mapped (project folder is root).  
 +
*alias: Path the base-name is mapped to on the server.
  
 +
In the example above the folder ".../&lt;project_name&gt;/resources" is mapped to the server root <code><nowiki>http://<url_to_rap_server>/</nowiki></code>.
  
=== How to switch language on user action? ===
+
=== How to specify context path when using the RAP launcher? ===
 +
Since RAP Tools 1.5 the context path could be specified in RAP launcher "Main" tab.
  
Switching the language in a running session isn't supported out of the box. So currently you'll have to restart the session to be able to switch the language of a running RWT application.  
+
'''Note:''' The solution below only applies only to earlier versions.
  
To restart the session, we shall send some Javascript code, which changes the <code>parent.window.location.href</code> DOM attribute of the current page. The new <code>parent.window.location.href</code> value is the URL of the current page plus a new parameter which contains the selected language code.
+
When starting an application with the RAP launcher (which is typical during the development), the URL of the RAP servlet is
  
==== Restarting the session and passing the new locale as a parameter ====
+
<code></code>
  
The <code>changeLocale</code> method is invoked when the user requests a language change (e.g. from a selection listener attached to some UI widget). In order to add page-reloading Javascript code to the HTTP response, we register a life-cycle <code>PhaseListener</code>:
+
  <nowiki>http://host:port/<servlet name>?startup=<entry point parameter></nowiki>
  
<source lang="java">
+
<br>  
  
  void changeLocale( String locale ) {
+
If the same application is deployed in a standalone application server, usually a non-empty ''context path'' will be added to the URL:
    final String url = createUrl( locale  );
+
    final Display display = Display.getCurrent();
+
    RWT.getLifeCycle().addPhaseListener( new PhaseListener() {
+
      private static final long serialVersionUID = 1L;
+
      public void afterPhase( PhaseEvent event ) {
+
        if( display == Display.getCurrent() ) {
+
          try {
+
            // Uses a non-public API, but currently this is the only solution.
+
            HtmlResponseWriter writer = ContextProvider.getStateInfo()
+
              .getResponseWriter();
+
            writer.write( "parent.window.location.href=\"" + url + "\";" );
+
          } catch( IOException e ) {
+
            e.printStackTrace();
+
          }
+
          RWT.getLifeCycle().removePhaseListener( this );
+
        }
+
      }
+
      public PhaseId getPhaseId() {
+
        return PhaseId.RENDER;
+
      }
+
    } );
+
  }
+
  
  private String createUrl( String locale ) {
+
<code></code>  
    StringBuffer url = new StringBuffer();
+
    url.append( RWT.getRequest().getContextPath() );
+
    url.append( RWT.getRequest().getServletPath() ); 
+
    url.append( "?locale=" );
+
    url.append( locale );
+
    return RWT.getResponse().encodeURL( url.toString() );
+
}
+
</source>
+
  
==== Getting the locale from the URL parameter ====
+
  <nowiki>http://host:port/<context path>/<servlet name>?startup=<entry point parameter></nowiki>
  
Before creating the UI, set the locale to the value, retrieved from the URL parameter:
+
<br>
  
<source lang="java">
+
Sometimes it is more convenient to have the same URLs (i.e. the same context paths) in both deployment scenarios. Typically, the ''context path'' for a web application is assigned by the system administrator when the application is deployed or is inferred from the war file name.  
public int createUI() {
+
  String locale = RWT.getRequest().getParameter( "locale" );
+
  if( locale != null ) {
+
    RWT.setLocale( new Locale( locale ) );
+
  }
+
  // Create the UI
+
  ...
+
}
+
</source>
+
  
=== How to specify context path when using the RAP launcher? ===
+
For the embedded Jetty servlet container that is used by the RAP launcher, the ''context path'' can be specified as a VM parameter in the launch configuration:
  
When starting an application with the RAP launcher (which is typical during the development), the URL of the RAP servlet is
+
<code></code>
  
<code>
+
   -Dorg.eclipse.equinox.http.jetty.context.path=&lt;context path&gt;
   <nowiki>http://host:port/<servlet name>?startup=<entry point parameter></nowiki>
+
</code>
+
  
If the same application is deployed in a standalone application server, usually a non-empty ''context path'' will be added to the URL:
+
<br>  
 
+
<code>
+
  <nowiki>http://host:port/<context path>/<servlet name>?startup=<entry point parameter></nowiki>
+
</code>
+
 
+
Sometimes it is more convenient to have the same URLs (i.e. the same context paths) in both deployment scenarios. Typically, the ''context path'' for a web application is assigned by the system administrator when the application is deployed or is inferred from the war file name.
+
  
For the embedded Jetty servlet container that is used by the RAP launcher, the ''context path'' can be specified as a VM parameter in the launch configuration:
+
In the launch configuration dialog, you should also change the '''Servlet name''' if the '''Open application in ...''' is checked.
  
<code>
+
Here is a complete example:
  -Dorg.eclipse.equinox.http.jetty.context.path=<context path>
+
</code>
+
  
In the launch configuration dialog, you should also change the '''Servlet name''' if the '''Open application in ...''' is checked.
+
*RAP launcher configuiration
  
Here is a complete example:
+
<code></code>
  
* RAP launcher configuiration
 
<code>
 
 
   VM argument: -Dorg.eclipse.equinox.http.jetty.context.path=/mycontext  
 
   VM argument: -Dorg.eclipse.equinox.http.jetty.context.path=/mycontext  
 
   Servlet name: mycontext/rap
 
   Servlet name: mycontext/rap
 
   Entry point: default
 
   Entry point: default
</code>
 
  
* Application URL
+
<br>  
<code> 
+
  <nowiki>http://127.0.0.1:<port>/mycontext/rap?startup=default</nowiki>
+
</code>
+
  
 +
*Application URL
 +
 +
<code></code>
 +
 +
  <nowiki>http://127.0.0.1:<port>/mycontext/rap?startup=default</nowiki>
  
=== How to update the UI from a background thread? ===  
+
=== How to update the UI from a background thread using Jobs? ===
  
Applications that wish to call UI code from a non-UI thread must provide a Runnable that calls the UI code. The methods <code>syncExec(Runnable)</code> and <code>asyncExec(Runnable)</code> in the <code>Display</code>class are used to execute these runnables in the UI thread during the event loop.
+
Applications that wish to call UI code from a non-UI thread must provide a Runnable that calls the UI code. The methods <code>syncExec(Runnable)</code> and <code>asyncExec(Runnable)</code> in the <code>Display</code> class are used to execute these runnables in the UI thread during the event loop.  
  
It is good practice to check if your widget is disposed from within the runnable when using <code>Display#asyncExec()</code>. Since other things can happen in the UI thread between the call to asyncExec and the execution of your runnable, you can never be sure what state your widgets are in by the time your runnable executes.
+
It is good practice to check if your widget is disposed from within the runnable when using <code>Display#asyncExec()</code>. Since other things can happen in the UI thread between the call to asyncExec and the execution of your runnable, you can never be sure what state your widgets are in by the time your runnable executes.  
  
It's a fairly typical situation when a UI element (e.g. <code>Table</code>) is populated with data retrieved from a database or some other external data source. The data source access code should not be ran in the UI thread in order to keep the UI responsive. The <code>org.eclipse.core.runtime.jobs</code> package provides an infrastructure for running concurrent operations (''jobs'') in a background threads.  
+
It's a fairly typical situation when a widget (e.g. <code>Table</code>) is populated with data retrieved from a database or some other external data source. The data source access code should not be ran in the UI thread in order to keep the UI responsive. The <code>org.eclipse.core.runtime.jobs</code> package provides an infrastructure for running concurrent operations (''jobs'') in a background threads.  
  
 
In the following example a job is created to fetch some data from a mocked-up data store and to show the result in a <code>Table</code>. Since the <code>Job</code> runs in the background, the update of the <code>TableViewer</code> will be made from a <code>Runnable</code> executed via <code>Display#asyncExec()</code>.  
 
In the following example a job is created to fetch some data from a mocked-up data store and to show the result in a <code>Table</code>. Since the <code>Job</code> runs in the background, the update of the <code>TableViewer</code> will be made from a <code>Runnable</code> executed via <code>Display#asyncExec()</code>.  
Line 847: Line 775:
 
   }
 
   }
 
}
 
}
</source>
+
</source>  
  
When using <code>Job</code>s, the UI updates are pushed automatically to the client because the <code>IProgressMonitor</code> takes care of activating the UICallBack mechanism. The article [[#How can my application push UI updates to the client without user interaction?]] shows how to achieve similar functionality in a standalone RWT application.
+
When using <code>Job</code>s, the UI updates are pushed automatically to the client because the <code>IProgressMonitor</code> takes care of activating the UICallBack mechanism. The article [[#How_to_update_the_UI_from_a_background_thread.3F]] shows how to achieve similar functionality in a standalone RWT application.  
  
=== How do I get notified when the browser window/tab is closed? ===
+
=== How to update the UI from a background thread  ===
  
RAP does not provide a notification when the client's browser window/tab is closed. There is no reliable way to detect such an event that works across all browser.
+
'''Note:''' Since RAP 2.0 <code>UICallBack</code> has been replaced with <code>ServerPushSession</code>. Use <code>ServerPushSession#start()</code> and <code>ServerPushSession#stop()</code> to activate/deactivate it.
Instead, the servlet session timeout is used to detect whether the user has left the application.
+
  
If there is no activity, the HTTP session will time out after a pre-set time. Upon session timeout, all objects that are held by the session (e.g. the Workbench, the Display) will be released.
+
To allow automatic UI-updates by server side background threads the ''UI Callback'' needs to be activated. Call <code>UICallBack#activate</code> before the start of a thread and <code>UICallBack#deactivate</code> at the end. Each activation needs a session unique identifier as a kind of reference pointer to be able to decide when all background threads are finished.  
  
 +
The following steps outline the pattern:
 +
 +
* activate the callback mechanism (must be done in the UI thread)
 +
* start background work
 +
* update the UI
 +
* deactivate the UI call-back.
 +
 +
The UI callback is activated by calling <code>UICallBack.activate( "callback id" )</code>, where the "callback id" is a session-unique identifier to trace the activation and deactivation. A typical use case is sketched below:
  
There are several ways to do some clean up before the session is destroyed. The first choice in most cases is to attach an <code>SWT.Close</code> listener to the Display or register a Runnable with disposeExec():
 
 
<source lang="java">
 
<source lang="java">
display.addListener( new Listener() {
+
UICallBack.activate( "callback id" );
 +
Thread bgThread = new Thread( bgRunnable );
 +
bgThread.setDaemon( true );
 +
bgThread.start();
 +
</source>
 +
 
 +
The actual changes to the UI must occur in the UI thread and are scheduled from the background thread by calling <code>display.asyncExec()</code>. As always is the case when updating UI from a background thread, we must check if the UI widget is not already disposed before using it. After the background work is done, we will disable the UI callback to free the aquired resources:
 +
 
 +
<source lang="java">
 +
Runnable bgRunnable = new Runnable() {
 +
  public void run() {
 +
    // do some work...
 +
    // schedule the UI update
 +
    display.asyncExec( new Runnable() {
 +
      public void run() {
 +
        if( !widget.isDisposed() ) {
 +
          // update the UI
 +
        }
 +
      }
 +
    } );
 +
    // Deactivate the UI call-back
 +
    display.asyncExec( new Runnable() {
 +
      public void run() {
 +
        UICallBack.deactivate( "callback id" );
 +
      }
 +
    } );
 +
  }
 +
};
 +
</source>
 +
 
 +
Technically, the UI callback is based on the long-polling technique. From the moment the <code>UICallBack.activate()</code> is called, the JavaScript client makes sure that there is always a pending request, waiting at the server to be processed. See the article [[RAP/UI Callback|Server push in RWT (aka UI Callback)]] for more details
 +
 
 +
'''Note''': if you are using the workbech APIs you may consider to [[#How_to_update_the_UI_from_a_background_thread_using_Jobs.3F|update the UI using Jobs]].
 +
 
 +
=== How do I get notified when the browser window/tab is closed?  ===
 +
 
 +
In most recent browsers, the UI session is automatically destroyed when the client's browser window/tab is closed. For older browsers, the HTTP session timeout is used to detect whether the user has left the application.
 +
 
 +
When UI session is destroyed, all objects that are held by it (e.g. the Workbench, the Display) will be released.
 +
 
 +
There are several ways to do some clean up before the UI session is destroyed. The first choice in most cases is to to register a <code>UISessionListener</code>:
 +
 
 +
<source lang="java">
 +
RWT.getUISession().addUISessionListener( new UISessionListener() {
 +
  public void beforeDestroy( UISessionEvent event ) {
 +
    // Perform cleanup       
 +
  }
 +
} );
 +
</source>
 +
 
 +
An alternative way is to attach an <code>SWT.Close</code> listener to the Display or register a Runnable with disposeExec(): <source lang="java">
 +
display.addListener( SWT.Close, new Listener() {
 
   public void handleEvent( Event event ) {
 
   public void handleEvent( Event event ) {
 
     // Perform cleanup         
 
     // Perform cleanup         
 
   }
 
   }
 
} );
 
} );
 +
 
display.disposeExec( new Runnable() {
 
display.disposeExec( new Runnable() {
 
   public void run() {
 
   public void run() {
Line 871: Line 857:
 
   }
 
   }
 
} );
 
} );
</source>
+
</source>  
  
An alternative way is to register a <code>SessionStoreListener</code>:
+
Note that when using the RAP launcher, by default a HTTP session never expires. To change the timeout value, adjust the setting on the "Main" tab.
 +
 
 +
The timeout value can also be set on a per session basis:  
  
 
<source lang="java">
 
<source lang="java">
RWT.getSessionStore().addSessionStoreListener( new SessionStoreListener() {
+
RWT.getUISession().getHttpSession().setMaxInactiveInterval(<timeout in Seconds>);  
  public void beforeDestroy( SessionStoreEvent event ) {
+
    // Perform cleanup       
+
  }
+
} );
+
 
</source>
 
</source>
  
Note that when using the RAP launcher, by default a session never expires. To change the timeout value, adjust the setting on the "Main" tab.
+
=== How do I implement a key binding that triggers a Command?  ===
 +
Since 1.4 M5, RAP supports the key bindings extension point (see bug <del>{{bug|282449}}</del> for details).
  
The timeout value can also be set on a per session baseis:
+
Since 1.4 M7, RAP supports key bindings in plain RWT applications by attaching a list of active key bindings to the display using <code>Display.setData</code> with <code>RWT.ACTIVE_KEYS</code> as key (see bug <del>{{bug|343248}}</del>).
 +
For details, please refer to the JavaDoc of <code>RWT.ACTIVE_KEYS</code>.
 +
 
 +
'''Note:''' The solution below only applies only to earlier versions.
 +
 
 +
First, you need to register some Javascript code to attach a key event listener to the qooxdoo ClientDocument. In this example, the key listener checks for the key sequence Ctrl+1 and sends a request with a custom request parameter if these keys are pressed.
  
 
<source lang="java">
 
<source lang="java">
RWT.getSessionStore().getHttpSession().setMaxInactiveInterval(<timeout in Seconds>);  
+
String jsCode = ""
 +
  + "var doc = qx.ui.core.ClientDocument.getInstance();\n"
 +
  + "doc.addEventListener( \"keydown\", function( evt ) {\n"
 +
  + "  var req = org.eclipse.swt.Request.getInstance();\n"
 +
  + "  if ( evt.isCtrlPressed() && evt.getKeyIdentifier() ==\"1\") {\n"
 +
  + "    req.addParameter( \""+ PARAMETER_KEY_PRESSED +"\", \"true\" );\n"
 +
  + "    req.send();\n"
 +
  + "  }\n"
 +
  + "} );\n";
 +
JSExecutor.executeJS( jsCode );
 +
</source>
 +
 
 +
A PhaseListener is registered that scans the request for the parameter and triggers the command if it was found:
 +
 
 +
<source lang="java">
 +
executeCommandPhaseListener = new ExecuteCommandPhaseListener( "my.command.id", PARAMETER_KEY_PRESSED );
 +
RWT.getLifeCycle().addPhaseListener( executeCommandPhaseListener );
 +
</source>
 +
 
 +
with executeCommandPhaseListener being an instance of the following class:
 +
 
 +
<source lang="java">
 +
public class ExecuteCommandPhaseListener implements PhaseListener {
 +
 
 +
  private final String commandId;
 +
  private final String parameter;
 +
 
 +
  public ExecuteCommandPhaseListener( String commandId, String parameter ) {
 +
    this.commandId = commandId;
 +
    this.parameter = parameter;
 +
  }
 +
 
 +
  public PhaseId getPhaseId() {
 +
    return PhaseId.READ_DATA;
 +
  }
 +
 
 +
  public void beforePhase( final PhaseEvent event ) {
 +
    // do nothing
 +
  }
 +
 
 +
  public void afterPhase( final PhaseEvent event ) {
 +
    HttpServletRequest request = RWT.getRequest();
 +
    String value = request.getParameter( parameter );
 +
    if( "true".equals( value ) ) {
 +
      ProcessActionRunner.add( new Runnable() {
 +
 
 +
        @Override
 +
        public void run() {
 +
          IHandlerService service
 +
            = (IHandlerService)PlatformUI.getWorkbench().getService( IHandlerService.class );
 +
          try {
 +
            service.executeCommand( commandId, null );
 +
          } catch( CommandException e ) {
 +
            throw new RuntimeException( e );
 +
          }
 +
        }
 +
      } );
 +
    }
 +
  }
 +
}
 +
</source>
 +
 
 +
That's it. The Command with the id ''my.command.id'' is triggered when the registered key event sent the parameter PARAMETER_KEY_PRESSED with the value "true". As an improvement, you could also listen for different key sequences to trigger different commands.
 +
 
 +
=== How to integrate Equinox Security/JAAS in RAP?  ===
 +
 
 +
Please read the [[RAP/Equinox Security Integration|Equinox Security Integration]] page
 +
 
 +
=== How to run a RAP application in multiple browser tabs/windows?  ===
 +
 
 +
Since RAP 2.1, multiple browser tabs are supported without any modifications (see {{bug|390236}} for details).
 +
The following discussion is still valid for RAP versions prior to 2.1.
 +
 
 +
By default, servlet engines use [http://en.wikipedia.org/wiki/HTTP_cookie#Session_management|session cookies] to identify a session. Browsers usually re-use the same session cookie for all tabs and windows within the same process. In the end, the RAP servlet cannot distinguish request that come from the same browser/client-session and rejects request that come from a second tab or window with the message "Multiple browser-instances or browser-tabs per session are not supported. You may click OK for restarting the session.". For a complete history of this issue, please see {{bug|285398}}.
 +
 
 +
The solution is to disable the use of cookies for session-identification of the servlet engine. For instruction on how to configure Tomcat and Jetty, read the sections below.
 +
 
 +
==== Tomcat  ====
 +
 
 +
(tested with version 5.5 and 6.0) In the <code>META-INF</code> folder of your web application, place a <code>context.xml</code> file with the following content: <source lang="xml">
 +
<Context cookies="false">
 +
</Context>
 +
</source> The cookies=false attribute causes Tomcat to not use cookies to identify a session, and rely only on URL rewriting by the application (RAP in this case).
 +
 
 +
Upon deployment of the WAR, the <code>context.xml</code> will be copied to an appropriate place under the <code>conf</code> folder. Once this file exists, it will not be replaced if a new WAR with a newer <code>META-INF/context.xml</code> is deployed. For further implications and more detailed information, please read the [http://tomcat.apache.org/tomcat-5.5-doc/config/context.html Apache Tomcat Configuration Reference]
 +
 
 +
The RAP Examples Demo uses the technique described here and can be [http://rap.eclipsesource.com/rapdemo viewed online] and [http://rap.eclipsesource.com/download/rapdemo.war downloaded].
 +
 
 +
==== Jetty  ====
 +
 
 +
This applies only to running RAP with standalone Equinox using the Jetty-based HttpService (e.g. launching a RAP application from within the IDE).
 +
 
 +
To enable tabbed browsing with Jetty, a so-called <code>JettyCustomizer</code> is needed. The <code>org.eclipse.rap.jettycustomizer</code> fragment bundle contains an implementation that instructs Jetty to not use cookies to identify a session, and rely only on URL rewriting by the application (RAP in this case).
 +
 
 +
For the customizer to be picked up when Jetty is started, the following system property must be specified: <code>-Dorg.eclipse.equinox.http.jetty.customizer.class=org.eclipse.rap.jettycustomizer.internal.SessionCookieCustomizer</code>
 +
 
 +
Currently, the fragment can be obtained from the [http://git.eclipse.org/c/rap/org.eclipse.rap.git/tree/bundles/org.eclipse.rap.jettycustomizer?h=streams/2.1-maintenance 2.1 branch in the RAP git repository].
 +
 
 +
=== How can i speed-up drawing on the GC?  ===
 +
 
 +
The RAP GC-implementation is only suited for relatively simple or static graphs. This is especially true for Internet Explorer which slows down significantly after about 200 drawing operations. Other browser can manage much more (depending on the clients hardware-resources), but will still need a very noticeable amount of time after a couple of hundred operations. Currently the only way to speed-up drawing is to reduce the number of drawing-operations as much as possible:
 +
 
 +
*Consider removing purely decorative elements.
 +
*If possible, use drawPolyLine instead of multiple drawLine operations.
 +
*If you have areas in your graph that change independently from each other, use one canvas for each of these areas instead of one big canvas.
 +
*If you have completely static elements, you can also set a background-image on the canvas and draw the dynamic elements above this image.
 +
 
 +
In future RAP-releases the performance might improve, but it will probably never be as fast as in SWT.<br>
 +
 
 +
'''Note:''' Since RAP 2.1 <code>Path</code> API is supported, which optimizes shapes drawing.
 +
 
 +
=== On my Tree or Table the item or cell background is not drawn when the row is hovered or selected. ===
 +
 
 +
This is the default behavior for all RAP versions until 1.4, and can not be changed. For RAP 1.5 this is still the default, but can be changed by theming.
 +
The default (or buisness) theme uses the TreeRow-Overlay/TableRow-Overlay elements to render the hover and selection state. These are painted over the item itself.
 +
 
 +
To change this, make a theme contribution are custom theme that looks like this:
 +
 
 +
<source lang="css">
 +
 
 +
/* First, overwrite the overlay to be always transparent. */
 +
Table-RowOverlay,
 +
Table-RowOverlay:hover,
 +
Table-RowOverlay:selected,
 +
TableItem:selected:unfocused,
 +
TableItem:linesvisible:even:hover,
 +
TableItem:linesvisible:even:selected,
 +
TableItem:linesvisible:even:selected,
 +
TableItem:linesvisible:even:selected:unfocused {
 +
  background-color: transparent;
 +
  color: inherit;
 +
  background-image: none;
 +
}
 +
 
 +
/* Now use the item instead of the overlay to create the hover and selection effects. */
 +
 
 +
TableItem:hover {
 +
  color: #4a4a4a;
 +
  background-color: #b5b5b5;
 +
  background-image: gradient(
 +
    linear, left top, left bottom,
 +
    from( #ebebeb ),
 +
    to( #d5d5d5 )
 +
  );
 +
}
 +
 
 +
TableItem:selected {
 +
  color: #ffffff;
 +
  background-color: #5882b5;
 +
  background-image: gradient(
 +
    linear, left top, left bottom,
 +
    from( #5882b5 ),
 +
    to( #416693 )
 +
  );
 +
}
 +
 
 +
TableItem:selected:unfocused {
 +
  color: #ffffff;
 +
  background-color: #b5b5b5;
 +
  background-image: gradient(
 +
    linear, left top, left bottom,
 +
    from( #b5b5b5 ),
 +
    to( #818181 )
 +
  );
 +
}
 +
 
 +
TableItem:linesvisible:even:hover {
 +
  color: #4a4a4a;
 +
  background-color: #b5b5b5;
 +
  background-image: gradient(
 +
    linear, left top, left bottom,
 +
    from( #ebebeb ),
 +
    to( #d5d5d5 )
 +
  );
 +
}
 +
 
 +
TableItem:linesvisible:even:selected {
 +
  color: #ffffff;
 +
  background-color: #5882b5;
 +
  background-image: gradient(
 +
    linear, left top, left bottom,
 +
    from( #5882b5 ),
 +
    to( #416693 )
 +
  );
 +
}
 +
 
 +
TableItem:linesvisible:even:selected:unfocused {
 +
  background-color: #959595;
 +
  background-image: gradient(
 +
    linear, left top, left bottom,
 +
    from( #b5b5b5 ),
 +
    to( #818181 )
 +
  );
 +
  color: #ffffff;
 +
}
 +
 
 
</source>
 
</source>
 +
 +
Tree is the same, just with TreeItem and TreeRow-Overlay. You can also mix and use overlay only for selection, but item for hover. In that case selection overwrites the item colors, but not the hover.
 +
 +
=== On my Tree or Table the selection or hover effect is hidden on items with background color. ===
 +
 +
This happens if you make a custom theme that does not use the TableRow-Overlay/TreeRow-Overlay element with RAP 1.5. In your custom theme, replace all occurrence of TableItem/TreeItem that use selection/hover states with TableRow-Overlay/TreeRow-Overlay.
 +
 +
=== How to make RAP applications Accessible ===
 +
RAP does not support the SWT accessibility API since it is not suitable for web applications. To be able to fulfill the [http://www.w3.org/TR/WCAG20/ Web Content Accessibility Guidelines] you will need to add [http://www.w3.org/TR/wai-aria/ ARIA] attributes to your application. You can do this (within limitations) using the web client API (RAP 2.3+ only, see [https://www.eclipse.org/rap/developers-guide/devguide.php?topic=scripting.html#html Developers Guide]), or - more thoroughly and comfortably - with the [http://eclipsesource.com/en/products/wai-aria-for-eclipse-rap/ Aria-Addon]. Please also read [http://eclipsesource.com/blogs/2014/05/28/rap-and-accessibility-screen-reader/ this] blog post on the topic.
 +
 +
=== How to create UI Tests for RAP ===
 +
Creating UI-Tests for RAP application is a challenge because nearly all widgets consist only of DIV elements with no stable IDs, making them difficult to identify. As of RAP 2.3, developer can [https://www.eclipse.org/rap/developers-guide/devguide.php?topic=scripting.html#html set HTML attributes] on any widget, making it easy to identifiy specific widget instances. Using the [http://eclipsesource.com/en/products/wai-aria-for-eclipse-rap/ Aria-Addon] (almost) all widgets automatically reflect their type and state in HTML attributes. Please also read [http://eclipsesource.com/blogs/2014/04/29/how-to-write-ui-tests-for-rap-with-selenium-2-0/ this] blog post on the topic.
 +
  
 
== Deployment  ==
 
== Deployment  ==
Line 895: Line 1,093:
 
=== Problems using Import-Package header  ===
 
=== Problems using Import-Package header  ===
  
Since RAP 1.1 the workbench introduced a concept of so-called "split packages". This is a way of OSGi to have virtual packages which are physically splitted across several bundles. See [http://eclipsesource.com/blogs/2008/08/22/tip-split-packages-and-visibility/ this post] and the OSGi 4.1 specification (§3.13.3) for more informations about split packages.  
+
Since RAP 1.1 the workbench introduced a concept of so-called "split packages". This is a way of OSGi to have virtual packages which are physically split across several bundles. See [http://eclipsesource.com/blogs/2008/08/22/tip-split-packages-and-visibility/ this post] and the OSGi 4.1 specification (§3.13.3) for more information about split packages.  
  
 
A common problem with them is that a split-package is only resolved when there is at least one additional split part available during runtime. As the RAP infrastrcuture only contains the workbench bundle at the moment this constrain is not met. In RCP world you often have the <code>org.eclipse.ui.ide</code> bundle available which contributes to these split packages and let Equinox resolve the constrains.  
 
A common problem with them is that a split-package is only resolved when there is at least one additional split part available during runtime. As the RAP infrastrcuture only contains the workbench bundle at the moment this constrain is not met. In RCP world you often have the <code>org.eclipse.ui.ide</code> bundle available which contributes to these split packages and let Equinox resolve the constrains.  
Line 909: Line 1,107:
 
</source>  
 
</source>  
  
The '''ui.workbench="split"''' directive tells Equinox to use only the "ui.workbench" part of this split package.  
+
The <code>ui.workbench="split"</code> directive tells Equinox to use only the "ui.workbench" part of this split package.
  
 
=== Exported WAR file does not work  ===
 
=== Exported WAR file does not work  ===
Line 917: Line 1,115:
 
*Check your <code>build.properties</code>  
 
*Check your <code>build.properties</code>  
 
**are you exporting the <code>plugin.xml</code>?  
 
**are you exporting the <code>plugin.xml</code>?  
**if you are using <code>plugin.properties</code> files, make sure they are exported
+
**if you are using <code>plugin.properties</code> files, make sure they are exported  
 
**are all libraries you are using listed on the plug-ins class path?  
 
**are all libraries you are using listed on the plug-ins class path?  
**Tip: As PDE build sometimes swallows error messages try exporting your feature with "Deployable feature" export, this may turn up error messages
+
**Tip: As PDE build sometimes swallows error messages try exporting your feature with "Deployable Feature" export, this may turn up error messages
  
*Enable the OSGi console by adding this init-param to the web.xml:
+
*Enable the OSGi console by adding this init-param to the <code>web.xml</code>:
  
 
<source lang="xml">
 
<source lang="xml">
Line 931: Line 1,129:
  
 
**you may want to add a port after -console in unix environments, you can then telnet to the OSGi console  
 
**you may want to add a port after -console in unix environments, you can then telnet to the OSGi console  
**type <code>ss</code> in the console and see if all bundles are started. If not try starting them with "start &lt;bundle-id&gt;". The stack traces may hint to what is missing
+
**type <code>ss</code> in the console and see if all bundles are started. If not try starting them with "start &lt;bundle-id&gt;". The stack traces may hint to what is missing  
 
**make sure that there is a <code>org.eclipse.equinox.servletbridge.extensionbundle</code> in the <code>ss</code>-listing whos state is <code>RESOVLED</code>
 
**make sure that there is a <code>org.eclipse.equinox.servletbridge.extensionbundle</code> in the <code>ss</code>-listing whos state is <code>RESOVLED</code>
  
*Make sure that the WAR does ''not'' contain the <code>javax.servlet</code> bundle. In the plug-in maniferst the <code>javax.servlet</code> '''must''' be listed in the <code>Import-Package</code> section, not in <code>Require-Bundle</code>.
+
*Make sure that the WAR does ''not'' contain the <code>javax.servlet</code> bundle because it is provided by the hosting servlet container. If your code depends on classes from <code>javax.servlet</code>, hte dependency must be expressed as an <code>Import-Package</code>. Do not list <code>javax.servlet</code> as a  <code>Require-Bundle</code>.
  
 
*Make sure that the WAR does ''not'' contain the <code>org.eclipse.update.configurator</code> bundle.
 
*Make sure that the WAR does ''not'' contain the <code>org.eclipse.update.configurator</code> bundle.
Line 946: Line 1,144:
 
**Select "The product configuration is based on '''features'''" on the "Overview" page  
 
**Select "The product configuration is based on '''features'''" on the "Overview" page  
 
**Add your feature to the list of features on the "Dependencies" page  
 
**Add your feature to the list of features on the "Dependencies" page  
**Press the ''Validate''' button in the top-left corner of the editor. Ignore complaints about missing <code>javax.servlet</code> packages
+
**Press the ''Validate''' button in the top-left corner of the editor. Ignore complaints about missing <code>javax.servlet</code> packages. Be aware that unresolved optional dependencies aren't reported as errors.
  
*Ifyou are re-deploying, make sure to delete the work directory of your servlet engine (e.g. <tomcat_install>/work/Catalina/localhost/work/<webapp_name> in Tomcat)
+
*If you are re-deploying, make sure to delete the work directory of your servlet engine (e.g. &lt;tomcat_install&gt;/work/Catalina/localhost/work/&lt;webapp_name&gt; in Tomcat)
  
 
=== How do I make remote calls to EJB's deployed on Jboss 4.x AS  ===
 
=== How do I make remote calls to EJB's deployed on Jboss 4.x AS  ===
Line 994: Line 1,192:
 
</source>  
 
</source>  
  
=== How do I use a RWT standalone application in Tomcat  ===
+
=== How do I develop an RWT standalone application with RAP <= 1.4 ===
 
+
 
To use the RWT standalone application in Tomcat follow the steps below:  
 
To use the RWT standalone application in Tomcat follow the steps below:  
  
 
*Create a new Java project.  
 
*Create a new Java project.  
 
*In the project create the folders:  
 
*In the project create the folders:  
**"WEB-INF"
+
** <code>WEB-INF</code>
**"WEB-INF/lib"
+
** <code>WEB-INF/lib</code>
**"WEB-INF/classes"
+
** <code>WEB-INF/classes</code>
**"WEB-INF/conf"
+
*In the <code>WEB-INF</code> folder, place a file named <code>web.xml</code> with the content below:
*In "WEB-INF" create a file <code>web.xml</code>:
+
 
+
 
<source lang="xml">
 
<source lang="xml">
<?xml version="1.0" encoding="ISO-8859-1"?>
+
<?xml version="1.0" encoding="UTF-8"?>
 
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
 
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Line 1,018: Line 1,213:
 
   <context-param>
 
   <context-param>
 
     <param-name>org.eclipse.rwt.entryPoints</param-name>
 
     <param-name>org.eclipse.rwt.entryPoints</param-name>
     <param-value>org.eclipse.rap.helloworld.HelloWorld</param-value>
+
     <param-value>com.example.HelloWorld</param-value>
 
   </context-param>
 
   </context-param>
  
Line 1,026: Line 1,221:
  
 
   <servlet>
 
   <servlet>
     <servlet-name>rapServlet</servlet-name>
+
     <servlet-name>rwtServlet</servlet-name>
 
     <servlet-class>org.eclipse.rwt.internal.engine.RWTDelegate</servlet-class>
 
     <servlet-class>org.eclipse.rwt.internal.engine.RWTDelegate</servlet-class>
 
   </servlet>
 
   </servlet>
  
 
   <servlet-mapping>
 
   <servlet-mapping>
     <servlet-name>rapServlet</servlet-name>
+
     <servlet-name>rwtServlet</servlet-name>
     <url-pattern>/main</url-pattern>
+
     <url-pattern>/</url-pattern>
 
   </servlet-mapping>
 
   </servlet-mapping>
 
</web-app>  
 
</web-app>  
Line 1,042: Line 1,237:
 
<context-param>
 
<context-param>
 
   <param-name>org.eclipse.rwt.themes</param-name>
 
   <param-name>org.eclipse.rwt.themes</param-name>
   <param-value>useradmin#theme/theme.properties</param-value>
+
   <param-value>useradmin#theme/theme.css</param-value>
 
</context-param>
 
</context-param>
  
Line 1,051: Line 1,246:
 
</source>  
 
</source>  
  
*In the <code>WEB-INF\conf</code> folder create a file <code>w4t.xml</code>:
+
*Copy the following jars from RAP in the <code>WEB-INF/lib</code> folder:  
 
+
<source lang="xml">
+
<?xml version="1.0" encoding="UTF-8"?>
+
<w4t:application xmlns:w4t="http://w4toolkit.com/"
+
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
  xsi:schemaLocation="http://w4toolkit.com/ w4t.xsd ">
+
  <initialisation>
+
    <lifecycle>org.eclipse.rwt.internal.lifecycle.RWTLifeCycle</lifecycle>
+
  </initialisation>
+
</w4t:application>   
+
</source>
+
 
+
*Copy the folloing jars from RAP in the <code>WEB-INF/lib</code> folder:  
+
 
**org.eclipse.rap.rwt  
 
**org.eclipse.rap.rwt  
**org.eclipse.rap.q07  
+
**org.eclipse.rap.q07 (only relevant for RAP <= 1.4)
 
*In case you want to make use of JFace, you also need to add these jars  
 
*In case you want to make use of JFace, you also need to add these jars  
 
**org.eclipse.core.commands  
 
**org.eclipse.core.commands  
Line 1,076: Line 1,258:
  
 
<source lang="java">
 
<source lang="java">
package org.eclipse.rap.helloworld;
+
package com.example;
  
 
import org.eclipse.rwt.lifecycle.IEntryPoint;
 
import org.eclipse.rwt.lifecycle.IEntryPoint;
Line 1,096: Line 1,278:
 
         display.sleep();
 
         display.sleep();
 
     }
 
     }
 +
    display.dispose();
 
     return 0;
 
     return 0;
 
   }
 
   }
Line 1,102: Line 1,285:
  
 
*ZIP the the <code>WEB-INF</code> folder (including the folder itself) as <code>RWT_Standalone.war</code>.  
 
*ZIP the the <code>WEB-INF</code> folder (including the folder itself) as <code>RWT_Standalone.war</code>.  
*Deploy the <code>RWT_Standalone.war&lt;code&gt; in Tomcat.</code>
+
*Deploy the <code>RWT_Standalone.war</code> in Tomcat.  
 
*Start the standalone RWT application from:
 
*Start the standalone RWT application from:
 
<pre>http://&lt;host&gt;:&lt;port&gt;/RWT_Standalone/main</pre>  
 
<pre>http://&lt;host&gt;:&lt;port&gt;/RWT_Standalone/main</pre>  
*If you get an &lt;code&gt;IllegalArgumentException or a <code>ClassNotFoundException</code> after deploying your application, please check first the contents of your WAR file. Maybe it doesn't contain all class files.
+
*If you get an <code>IllegalArgumentException</code> or a <code>ClassNotFoundException</code> after deploying your application, please check first the contents of your WAR file. Maybe it doesn't contain all class files.
  
=== How do I use a RAP standalone application with an embedded server ===
+
=== How do I develop an RWT standalone application with RAP >= 1.5 ===
 +
It is recommended to use the [http://eclipse.org/rap/downloads RAP Tooling] for developing RWT applications. Though it is not stricly necessary to use the tooling, it eases development with a launch configuration tailored for RWT applications and documentation.
  
In order to demonstrate the usage of RAP without the integration of the application in an existing server environment a RAP standalone application can be created as follows.
+
Follow the steps outlined below and you will have a simple web application up and running in a few minutes.
# Add the following plugins to the RAP target runtime or create a new target runtime including the two plugins and your "old" RAP target runtime
+
* Create a Java Project (or a plug-in project if you prefer and if you are familiar with plug-in development)
## org.eclipse.equinox.launcher
+
* Configure the project to match the layout of a web application. You may skip or postpone this step if you are using RAP Tooling to launch the application. The layout is neccessary if you want to deploy the project as a WAR.
## org.eclipse.equinox.launcher.(win/linux/...) depending on your operating system.  
+
** Create the three folders: <code>WEB-INF</code>, <code>WEB-INF/lib</code>, <code>WEB-INF/classes</code>
# Rebuild your project with this new target runtime
+
** Change the projects' output folder to <code>WEB-INF/classes</code>.
# Create a new "product configuration" file (i.e. standalone.product) and open it with the procut configuration file editor.  
+
* Copy the <code>org.eclipse.rap.rwt_*</code> jar from the [http://eclipse.org/rap/downloads RAP Runtime] into the <code>WEB-INF/lib</code> folder and add it to the projects' build path. The <code>org.eclipse.rap.rwt.source_*</code> jar contains the RWT source code. To be able to browse the sources and read JavaDoc, specify this jar as the [http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Freference%2Fref-properties-source-attachment.htm Source Attachment]
# On the "Overview" page enter the following values
+
* Implement an <code>IEntryPoint</code> like below:
## Name: StandAlone
+
<source lang="Java">
## Product: Select the plugin project containing the product definition with the "New..." Button.
+
public class HelloWorld implements IEntryPoint {
## Application: org.eclipse.ui.workbench
+
  public int createUI() {
# On the "Dependency" page enter the plugins and fragments required. Don't forget the jetty server and the rap plugins.
+
    Display display = new Display();
# On the "Configuration" page set the start level and the auto-start behaviour. This takes its time.
+
    Shell shell = new Shell( display );
# On the "Launching" you can name the launcher (i.e. StandAlone)
+
    shell.setLayout( new GridLayout() );
# Save the file and go back to the Overview page.  
+
    Label label = new Label( shell, SWT.NONE );
# Use the "Eclipse Product export wizard" in order to create a RCP/RAP launcher in a dedicated directory.
+
    label.setText( "Hello RAP World" );
 +
    shell.setSize( 500, 400 );
 +
    shell.open();
 +
    return 0;
 +
  }
 +
}
 +
</source>
 +
With the RAP Tooling installed, you can already launch your HelloWorld application. To do so, select the HelloWorld class (i.e. in the Package Explorer) and choose Run As &gt; RWT Application from the context menu.
 +
 
 +
If you whish to deploy your application on an external servlet engine, or if you need a deployment descriptor for other reasons, or if you haven't installed the RAP Tooling, a few more steps are required to run the application.
 +
* Place a deployment descriptor (<code>web.xml</code>) in the <code>WEB-INF</code> folder with the content below:
 +
<source lang="xml">
 +
<?xml version="1.0" encoding="UTF-8"?>
 +
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
 +
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 +
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
 +
  version="2.4">
 +
 
 +
  <context-param>
 +
    <param-name>org.eclipse.rap.applicationConfiguration</param-name>
 +
    <param-value>com.example.HelloWorldConfiguration</param-value>
 +
  </context-param>
 +
 
 +
  <listener>
 +
    <!-- For RAP 2.x, use org.eclipse.rap.rwt.engine.RWTServletContextListener instead -->
 +
    <listener-class>org.eclipse.rwt.engine.RWTServletContextListener</listener-class>
 +
  </listener>
 +
 
 +
  <servlet>
 +
    <servlet-name>rwtServlet</servlet-name>
 +
    <!-- For RAP 2.x, use org.eclipse.rap.rwt.engine.RWTServlet instead -->
 +
    <servlet-class>org.eclipse.rwt.engine.RWTServlet</servlet-class>
 +
  </servlet>
 +
 
 +
  <servlet-mapping>
 +
    <servlet-name>rwtServlet</servlet-name>
 +
    <url-pattern>/hello</url-pattern>
 +
  </servlet-mapping>
 +
</web-app>
 +
</source>
 +
* Provide an implementation of <code>ApplicationConfigurator</code> to configure your application like shown below
 +
<source lang="Java">
 +
public class HelloWorldConfiguration implements ApplicationConfiguration {
 +
 
 +
  @Override
 +
  public void configure( Application application ) {
 +
    Map<String, String> properties = new HashMap<String, String>();
 +
    properties.put( WebClient.PAGE_TITLE, "Hello world" );
 +
    application.addEntryPoint( "/hello", HelloWorld.class, properties );
 +
  }
 +
 
 +
}
 +
</source>
 +
* Again you can use the RAP Tooling to launch the application from the just created <code>web.xml</code>. To do so, create a new ''RWT Launch Configuration'' and select "Run from web.xml". Enter the location of the web.xml file and specify "hello" as the servlet path.
 +
 
 +
You may also find the [http://wiki.eclipse.org/index.php/JFace JFace] components useful. In order to use them from RWT standalone, you will need to add the following jars from the [http://eclipse.org/rap/downloads RAP Runtime]:
 +
* <code>org.eclipse.rap.jface</code>
 +
* <code>org.eclipse.core.runtime</code>
 +
* <code>org.eclipse.core.commands</code>
 +
* <code>org.eclipse.equinox.common</code>
  
 
=== Why are there encoding issues after deploying my RAP application as a WAR?  ===
 
=== Why are there encoding issues after deploying my RAP application as a WAR?  ===
Line 1,189: Line 1,432:
 
=== Why is IE6 so slow? What can be done?  ===
 
=== Why is IE6 so slow? What can be done?  ===
  
The JScript engine used in Internet Explorer 6 - Microsoft Windows Script 5.7 - is known for being slow with AJAX applications. The solutions, suggested by Microsoft are:  
+
Please note that IE6 is no longer supported as of RAP 1.5.
 +
 
 +
The JScript engine used in Internet Explorer 6 - Microsoft Windows Script 5.7 - is known for being slow with Ajax applications. The solutions, suggested by Microsoft are:  
  
 
*if you can upgrade to Microsoft Windows Script 5.7 - apply hotfix [http://support.microsoft.com/kb/919237/ 919237];  
 
*if you can upgrade to Microsoft Windows Script 5.7 - apply hotfix [http://support.microsoft.com/kb/919237/ 919237];  
 
*if you have to stay with Microsoft Windows Script 5.6 - apply hotfix [http://support.microsoft.com/kb/942840/ 942840] which includes an improved garbage collector.
 
*if you have to stay with Microsoft Windows Script 5.6 - apply hotfix [http://support.microsoft.com/kb/942840/ 942840] which includes an improved garbage collector.
  
 +
<br> For more information on this topic, please take a look at the following links:
  
For more informations on this topic, please take a look at the following links:
+
*http://blogs.msdn.com/jscript/archive/2007/11/29/jscript-performance-update-for-ie6-users.aspx  
* http://blogs.msdn.com/jscript/archive/2007/11/29/jscript-performance-update-for-ie6-users.aspx
+
*http://support.microsoft.com/kb/942840/en-us  
* http://support.microsoft.com/kb/942840/en-us
+
*http://support.microsoft.com/kb/919237/en-us  
* http://support.microsoft.com/kb/919237/en-us
+
*http://erik.eae.net/archives/2007/12/14/20.07.27/
* http://erik.eae.net/archives/2007/12/14/20.07.27/
+
  
=== Where to find translations/language packs? ===
+
=== Where to find translations/language packs? ===
  
Translations for RAP can be found at the [http://www.eclipse.org/babel/downloads.php Babel project]:
+
Translations for RAP can be found at the [http://www.eclipse.org/babel/downloads.php Babel project]: It provides community translated language packs for the majority of the Eclipse projects.  
It provides community translated language packs for the majority of the Eclipse projects.
+
  
== Branding and Theming ==
+
=== How do I use GZip compression with the Equinox HttpService? ===
 +
Contemporary servlet engines provide for transparently compressing the response content. However, the methods used to enable this feature are container-specific. Here is an example for a Tomcat connector declaration in  <code>server.xml</code>:
  
=== How can I use an image in the branding body? ===
+
<source lang="xml">
 +
<Connector port="8080"
 +
          ...
 +
          compression="on"
 +
          compressionMinSize="1024"
 +
          compressableMimeType="text/html,text/xml,text/plain,text/javascript,application/json"/>
 +
</source>
  
Using a user-defined resource inside of a RAP application requires registering it in some way. In case of images used inside the branding body, this can be done declaratively using the <code>org.eclipse.equinox.http.registry.resources</code> extension.
+
When deploying RAP as standalone  server (as opposed to deploying it in a  WAR) the servlet engine is ''hidden'' behind the HttpService.
  
Assume you have a folder <code>branding</code> in the root of your plugin, containing an image called <code>loading.gif</code>. Add the following code to your <code>plugin.xml</code>:
+
RAP by default ships with Equinox for the OSGi implementation and Equinox in turn uses Jetty behind the HttpService. This allows to configure the Jetty GzipFilter. The following applies to RAP running on Equinox 3.6 or later and Jetty 6.2.x.
 +
 
 +
To register the filter, use the extension below in your <code>plugin.xml</code>
 +
 
 +
<source lang="xml">
 +
<extension point="org.eclipse.equinox.http.registry.filters">
 +
    <filter
 +
        alias="/"
 +
        class="org.mortbay.servlet.GzipFilter"
 +
        httpcontextId="org.eclipse.rap.httpcontext"
 +
        load-on-startup="true">
 +
    </filter>
 +
</extension>
 +
</source>
 +
 
 +
For the GzipFilter class to be found, the plug-in that defines the extension must depend on <code>org.mortbay.jetty.util</code>
 +
 
 +
For those who prefer using OSGi services the filter can also be registered with the <code>org.eclipse.equinox.http.servlet.ExtendedHttpService</code>.
 +
 
 +
=== How to add a custom error page to a web application ===
 +
 
 +
The Equinox BridgeServlet has to be registered with a ''path mapping'', i.e. a pattern that begins with a <code>/</code> and ends with a <code>/*</code>.
 +
Usually, it is mapped to <code>/*</code>. But this mapping prevents any other path mappings from taking effect.
 +
 
 +
To add a custom error page to an application, the BridgeServlet can be mapped to a path like <code>/foo/*</code>.
 +
Here's an example:
 +
 
 +
<source lang="xml">
 +
<servlet-mapping>
 +
  <servlet-name>equinoxbridgeservlet</servlet-name>
 +
  <url-pattern>/app/*</url-pattern>
 +
</servlet-mapping>
 +
 
 +
<error-page>
 +
  <error-code>404</error-code>
 +
  <location>/error404.html</location>
 +
</error-page>
 +
</source>
 +
 
 +
Your web application will then be available at the following URL:
 +
<pre>http://&lt;host&gt;:&lt;port&gt;/&lt;context_path&gt;/app/&lt;servlet_path&gt;</pre>
 +
 
 +
The HTTP 404 error code will be served by your custom error page <code>error404.html</code>, which resides in the root directory of the webapp.
 +
 
 +
== Branding and Theming  ==
 +
 
 +
=== How to activate the Classic Theme?  ===
 +
 
 +
'''Note''': The classic theme is no longer part of RAP 2.x.
 +
 
 +
To activate the classic theme add the bundle <code>org.eclipse.rap.rwt.theme.classic</code> to an exsiting launch configuration and change the servlet name to "classic". An alternative way to use the classic theme is to create an own branding and set the <code>themeId</code> to <code>org.eclipse.rap.rwt.theme.classic</code>.
 +
 
 +
=== How can I use an image in the branding body?  ===
 +
 
 +
Using a user-defined resource inside of a RAP application requires registering it in some way. In case of images used inside the branding body, this can be done declaratively using the <code>org.eclipse.equinox.http.registry.resources</code> extension.
 +
 
 +
Assume you have a folder <code>branding</code> in the root of your plugin, containing an image called <code>loading.gif</code>. Add the following code to your <code>plugin.xml</code>:  
  
 
<source lang="xml">
 
<source lang="xml">
Line 1,221: Line 1,528:
 
     </resource>
 
     </resource>
 
</extension>
 
</extension>
</source>
+
</source>  
  
This maps the provided resource base name to the given alias name and makes it available for use.
+
This maps the provided resource base name to the given alias name and makes it available for use.  
  
Access the image in your branding body by referring to the alias name:
+
Access the image in your branding body by referring to the alias name:  
  
 
<source lang="html4strict">
 
<source lang="html4strict">
Line 1,231: Line 1,538:
 
     <img src="./loading.gif" border="0" />
 
     <img src="./loading.gif" border="0" />
 
...
 
...
</source>
+
</source>  
  
=== Why doesn't RAP simply use CSS for styling, such as e.g. GWT? ===
+
=== Why doesn't RAP simply use native CSS for styling? ===
  
In GWT for example, every widget has a configurable style name that is rendered as class attribute.
+
We aim to support native CSS documents at one point, but there are some obstacles to consider:
Styling is done by adding a CSS file to the target HTML document.
+
Although this is a simple technique, it does not work out for RAP for a number of reasons:
+
  
1. One of the main objectives of RAP is to prevent vendor lock-in by allowing for exchangeable client implementations.
+
1. RAP allows for exchangeable client implementations (such as Tabris). Not all of those technologies can necessarily deal with CSS.  
Although our current client-side technique is based on JavaScript, there are completely different technologies out there that might be interesting candidates for RAP clients (GWT, Flex, Silverlight ...).
+
Not all of those technologies can necessarily deal with CSS.
+
  
2. In RAP, all layouts are computed on the server side, thus the server also needs to know about any variable dimensions like paddings and borders.
+
2. In RAP, all layouts are computed on the server side, thus the server also needs to know about any variable dimensions like paddings and borders. This is not possible if a style sheet is applied only on the client side.  
This is not possible if a style sheet is applied only on the client side.
+
  
3. Due to browser bugs and differences, writing cross-browser CSS code is a complex task.
+
3. Due to browser bugs and differences, writing cross-browser CSS code is a complex task. Currently RAP works around these issues by parsing the CSS document at applying the values itself.
Client technologies may work around those browser issues. In fact, the qooxdoo library does so.
+
  
=== How can I change the favicon / title of my RAP app? ===
+
=== How can I change the favicon / title of my RAP app? ===
  
 
With the help of RAPs branding features you're able to define several (visual) aspects of your RAP application. This includes for example the page title which is shown in the browser, the favicon or the theme to be used for the application. See the [http://help.eclipse.org/ganymede/topic/org.eclipse.rap.help/help/html/advanced/branding.html Branding section] of the RAP Developer guide for details.  
 
With the help of RAPs branding features you're able to define several (visual) aspects of your RAP application. This includes for example the page title which is shown in the browser, the favicon or the theme to be used for the application. See the [http://help.eclipse.org/ganymede/topic/org.eclipse.rap.help/help/html/advanced/branding.html Branding section] of the RAP Developer guide for details.  
  
 +
== Single Sourcing  ==
 +
 +
=== Why does <code>Display#getDefault()</code> work different than in SWT  ===
 +
 +
One notable difference between SWT and RWT is the way the UI thread is determined. In SWT, calling <code>new Display()</code> marks the thread that executes the code as the UI thread. This means that the programmer is free to choose which thread should become the UI thread.
 +
 +
RWT, on the contrary, already provides the one and only UI thread before <code>createUI</code> is called (the equivalent to the <code>main</code> method). As a consequence, <code>getDefault()</code> can only create a Display instance if it is called from the user-interface thread.
 +
 +
=== How do I setup dependencies on org.eclipse.rap.ui bundles for single sourcing?  ===
 +
 +
The shared package structure between org.eclipse.ui and org.eclipse.rap.ui bundles requires that dependencies be specified either via <code>package-import</code> or <code>require-bundle</code> in the bundle manifest. When single sourcing using <code>require-bundle</code>, it is necessary to mark the bundles as ''optional'' that are specific to either target platform, (e.g., org.eclipse.ui and org.eclipse.rap.ui). When using <code>package-import</code> be cautious of ''split-packages'', see [http://dev.eclipse.org/newslists/news.eclipse.technology.equinox/msg05572.html this thread] for more info.
 +
 +
=== Why can't I remove warnings for missing org.eclipse.ui bundles?  ===
 +
 +
Using <code>require-bundle</code> as mentioned above will produce warnings regarding the missing optional bundles. One method of removing these warnings is to create empty bundles that have the same bundle name and version so that they act as stubs for those that are missing.
 +
 +
Example org.eclipse.ui:
 +
 +
1. Create a manifest file for the stub bundle:
 +
 +
<code></code>
 +
 +
    Manifest-Version: 1.0
 +
    Bundle-Version: 3.5.1.200812160941_stub
 +
    Bundle-Name: RCP UI fake bundle
 +
    Bundle-ManifestVersion: 2
 +
    Bundle-SymbolicName: org.eclipse.ui
 +
    Bundle-RequiredExecutionEnvironment: JavaSE-1.6
 +
 +
<br>
 +
 +
2. Place the file in a META-INF directory and zip it up.
 +
 +
3. Name the bundle '''org.eclipse.ui_3.5.1.200812160941_stub.jar'''
 +
 +
4. Place the bundle in your RAP target platform and refresh it.
 +
 +
Now the warnings should be gone for that bundle.
 +
 +
== Troubleshooting ==
 +
 +
=== My application is not running after switching to RAP 1.5 ===
 +
 +
The RAP 1.5 runtime contains an additional bundle named <code>org.eclipse.rap.rwt.osgi</code>.
 +
You need to include it in your launch configuration to be able to run a workbench-based application.
 +
 +
The Equinox console also requires three new bundles from the Apache Gogo project:
 +
<code>org.apache.felix.gogo.command</code>,
 +
<code>org.apache.felix.gogo.runtime</code>, and
 +
<code>org.apache.felix.gogo.shell</code>.
 +
These new bundles are contained in the basic RAP target.
 +
 +
=== IllegalStateException: No context available outside of the request service lifecycle  ===
 +
 +
Your code tries to access session-information but isn't run from the UI thread (the only thread that has a session context associated by default).
 +
 +
The soltion is to wrap your code in a Runnable and have it executed by <code>UICallBack#runNonUIThreadWithFakeContext()</code>.
 +
 +
Since RAP 2.0 use <code>RWT.getUISession( Display ).exec( Runnable )</code> instead.
 +
 +
Please also see this thread: [http://dev.eclipse.org/newslists/news.eclipse.technology.rap/msg01311.html]
 +
More on session scope and session singletons can be found in the section on scopes and data stores in RWT in the RAP Developer's Guide.
 +
[edit]
 +
 +
=== NamespaceException: The alias '/rwt-resources' is already in use ===
 +
 +
This means that more than one application is started in the same context and in the same HttpService.
 +
When different applications run in the same HttpService, they have to use different context paths.
 +
 +
This also happens when an ApplicationConfiguration is registered AND the org.eclipse.rap.workbench bundle is running (see [https://bugs.eclipse.org/bugs/show_bug.cgi?id=377414 bug 377414]).
 +
 +
To register an ''ApplicationConfiguration'' on a custom context using declarative services, add the following line to the service component definition:
 +
 +
<source lang="xml">
 +
  <property name="contextName" type="String" value="example"/>
 +
</source>
 +
 +
See also <code>ApplicationLauncher#PROPERTY_CONTEXT_NAME</code>.
 +
 +
=== Application is not starting with "org is not defined" error in JS console  ===
 +
 +
This usually points to a syntax error in JS, e.g. a missing comma between two members or a superfluous comma after the last member. Example: <code></code>
 +
 +
    members&nbsp;: {
 +
   
 +
    doSth&nbsp;: function() {
 +
   
 +
    }, // comma!
 +
   
 +
    doSthDifferent&nbsp;: function() {
 +
   
 +
    } // no comma!
 +
    }
 +
 +
Unfortunately, Firefox and Chrome seem to more tolerant with these errors than Internet Explorer.
 +
 +
=== Locale fallback does not work as expected  ===
 +
 +
You created a messages.properties file and several messages_XX.properties files for different locales. You expect the system to use the messages.properties file as a fallback when no messages_XX.file is found for the locale in question.
 +
 +
But that's not how RWT works. If a properties file is not found for the locale in question, RWT tries the default locale, i.e. <code>Locale.getDefault()</code>. This is usually the VM's locale. So if your VM is set to German, and there is a messages_de.properties file, you'll get German. If you want to have English as your fallback, set your VM to English.
 +
 +
=== Widgets disappearing in Google Chrome, Safari or Opera ===
 +
This is most likely caused by a bug in the Webkit engine, as discussed in [https://bugs.eclipse.org/bugs/show_bug.cgi?id=428717 Bug 428717 - Invisible Elements in RAP Demo with latest WebKit based browsers]. In summary, the issue arises under some constallations if Webkit uses hardware accelerated rendering for some of the documents element (usually Canvas), but not all. The bug has been reported to the Webkit developers [https://bugs.webkit.org/show_bug.cgi?id=125070 here].
 +
 +
'''The issue was fixed in RAP 2.3M3.''' For previous versions of RAP, there are multiple alternative workarounds:
 +
 +
* Disable Hardware Acceleration in your Browser
 +
With disabled hardware acceleration this issue doesn't exist, but there can be some small impact on the performance of Canvas. In Chrome the acceleration can be disabled by visiting the URL "chrome://flags/". Look for options mentioning "GPU" and enable/disable them as needed.
 +
 +
* Force Hardware Acceleration for the entire Page
 +
In the entry point of your application, add the following lines:
 +
 +
    JavaScriptExecutor jse = RWT.getClient().getService( JavaScriptExecutor.class );
 +
    jse.execute( "var style = document.createElement( \"style\" );\n" +
 +
                "style.type = 'text/css';\n" +
 +
                "style.innerHTML = \"div { -webkit-transform: translate3d(0, 0, 0); }\";\n" +
 +
                "document.getElementsByTagName( \"head\" )[ 0 ].appendChild( style );" );
 +
 +
 +
'''Note''' that there have been reports of this workaround to cause other visual glitches (specifically in Chrome 35+), and the impact on performance is unkown.
 +
Overall it is not recommended. At the very least you should limit the hack it to webkit versions that have been tested (using the navigator string).
 +
 +
* Use Background Gradients in your theme instead of Background Colors
 +
Somehow the issue is connected to the usage of the CSS attribute background-color on Composites, but does not appear when using gradients. Therefore, one relatively simple solution is to always set a gradient when setting background color, using the same values. To do so, you need to register a [http://eclipse.org/rap/developers-guide/devguide.php?topic=theming.html theme contribution] with the following content:
 +
 +
  Composite, Group {
 +
    background-image: gradient(
 +
      linear, left top, left bottom,
 +
      from( #ffffff ),
 +
      to( #ffffff )
 +
    );
 +
  }
 +
 +
However, if you use a theme contribution/custom theme with custom variants to set the background colors of Composites you need to updated those also. Wherever you set a background color other than white you also need to set a background gradient with the same colors. (Otherwise the above rule overwrites that color.)
 +
 +
Example:
 +
 +
  .header {
 +
    background-color: #31619C;
 +
    background-image: gradient(
 +
      linear, left top, left bottom,
 +
      from( #31619C ),
 +
      to( #31619C )
 +
    );
 +
  }
 +
 +
In case of transparent background, it has to look like this:
 +
 +
  .header {
 +
    background-color: transparent;
 +
    background-image: none;
 +
  }
  
== Single Sourcing ==
+
=== Blank page or client crash in Firefox 29+  ===
 +
This is likely caused by "[https://bugs.eclipse.org/bugs/show_bug.cgi?id=433179 Bug 433179 - RAP application does not start in Firefox 29 (beta) ]".
 +
It was fixed in RAP 2.3M3. For previous RAP versions, follow these steps:
  
=== Why does <code>Display#getDefault()</code> work different than in SWT===
+
If your application is started by [http://eclipse.org/rap/developers-guide/devguide.php?topic=application-configuration.html&version=2.2 ApplicationConfiguration], add the follwing line there:
 +
      properties.put( WebClient.HEAD_HTML, "<script type=\"text/javascript\">(function(){try{var e=navigator.userAgent;if(e.indexOf(\"like Gecko\")===-1&&e.indexOf(\"Gecko/\")!==-1){if(!window.controllers){window.controllers={}}if(!navigator.product){navigator.product=\"Gecko\"}}}catch(t){}})()</script>" );
  
One notable difference between SWT and RWT is the way the UI thread is
+
If your application is started by an org.eclipse.rap.ui.entrypoint extension, create a [http://eclipse.org/rap/developers-guide/devguide.php?topic=branding.html&version=2.2 branding] to add a body HTML file (in case you don't have one already). In this HTML file add the following:
determined.
+
  <script type="text/javascript">(function(){try{var e=navigator.userAgent;if(e.indexOf("like Gecko")===-1&&e.indexOf("Gecko/")!==-1){if(!window.controllers){window.controllers={}}if(!navigator.product){navigator.product="Gecko"}}}catch(t){}})()</script>
In SWT, calling <code>new Display()</code> marks the thread that executes
+
the code as the UI thread. This means that the programmer is free to
+
choose which thread should become the UI thread.  
+
  
RWT, on the contrary,
 
already provides the one and only UI thread before <code>createUI</code>
 
is called (the equivalent to the <code>main</code> method).
 
As a consequence, <code>getDefault()</code> can only creae a Display instance
 
if it is called from the user-interface thread.
 
  
 +
=== Scrolling looks broken in Google Chrome, Safari or Opera ===
 +
This is similar to [[RAP/FAQ#Widgets disappearing in Google Chrome, Safari or Opera]]. The workaround "Force Hardware Acceleration for the entire Page" mentioned there also fixes this issue.
 +
At this time the exact cause is not known, but it is very likely related to hardware acceleration. We hope to find a propper fix for this issue to apply it to RAP 3.0 This is also discussed in [https://bugs.eclipse.org/bugs/show_bug.cgi?id=435200 this bug report].
  
[[Category:FAQ]]
+
[[Category:FAQ]] [[Category:RAP]]
[[Category:RAP]]
+

Revision as of 11:54, 8 July 2014

This FAQ is always evolving. Please also scan the RAP newsgroup archives for answers.

Contents

Writing RAP Applications

I found a bug in RAP. Where should I report it?

Please report bugs or feature request in the Eclipse Bugzilla. See our Bugs page for details.

How to create a fullscreen application

  • use the style-bit SWT.NO_TRIM before creating the Shell. This will remove the title-bar, the minimize/maximize/close buttons and the ability for manually resizing the window.
  • overwrite the WorkbenchWindowAdvisor.postWindowCreate() method. Here we set the state of the Shell to maximized.
  • optional: depending on the requirements and complexity of our application it may be desirable to hide the menu bar by calling getWindowConfigurer().setShowMenuBar( false ). This is usually a good choice for small applications where the menu bar may not be needed.
public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {
 
  // ...
 
 public void preWindowOpen() {
   // ...
   getWindowConfigurer().setShellStyle( SWT.NO_TRIM );
   getWindowConfigurer().setShowMenuBar( false );
 }
 
 public void postWindowCreate() {
   Shell shell = getWindowConfigurer().getWindow().getShell();
   shell.setMaximized( true );
 }
}
Take a look here for a more detailed discussion of the topic.

How do I add an applet / flash / an existing Javascript libary

  • A very simplistic approach is to create a html page (or a servlet) containing your applet / flash / JS. You can simply use the browser widget to load that page within your application.

How to access the request parameters?

Let's suppose that the request URI looks like the following:

 http://www.example.com:8080/foo?var1=value1&var2=value2


To snippet below will retrieve the values of request parameters var1 and var2.

   HttpServletRequest request = RWT.getRequest();
   String var1 = request.getParameter( "var1" );
   String var2 = request.getParameter( "var2" );


How to access the HTTP session / session store?

Applies to RAP < 2.0

In RAP, the sessoin store is the preferred way to store information in the session context. However it also provides access to the HTTP session (see the code below). For working with session singletons, please use the SessionSingletonBase class.

Access the HTTP session / session store from the UI thread: HttpSession session = RWT.getSessionStore().getHttpSession();

Access the HTTP session / session store from a background thread:
final Display display = new Display();
final Runnable bgRunnable = new Runnable() {
  public void run() {
    UICallBack.runNonUIThreadWithFakeContext( display, new Runnable() {
      public void run() {
        Object someValue = RWT.getSessionStore().getAttribute( "myAtt" );
        System.out.println( someValue );
        // access the HTTP session
        RWT.getSessionStore().getHttpSession();
      }
    } );
  }
};
Shell shell = new Shell( display );
Button button = new Button( shell, SWT.PUSH );
button.setText( "Start Background Thread" );
button.addSelectionListener( new SelectionAdapter() {
  public void widgetSelected( final SelectionEvent evt ) {
    RWT.getSessionStore().setAttribute( "myAtt", "someValue" );
    Thread thread = new Thread( bgRunnable );
    thread.setDaemon( true );
    thread.start();
  }
} );

Applies to RAP >= 2.0

Access the HTTP session / UI session from a background thread:
final Display display = new Display();
final Runnable bgRunnable = new Runnable() {
  public void run() {
    RWT.getUISession( display ).exec( new Runnable() {
      public void run() {
        Object someValue = RWT.getUISession().getAttribute( "myAtt" );
        System.out.println( someValue );
        // access the HTTP session
        RWT.getUISession().getHttpSession();
        // access session singleton
        SingletonUtil.getSessionInstance( MyClass.class );
      }
    } );
  }
};
Shell shell = new Shell( display );
Button button = new Button( shell, SWT.PUSH );
button.setText( "Start Background Thread" );
button.addSelectionListener( new SelectionAdapter() {
  public void widgetSelected( final SelectionEvent evt ) {
    RWT.getUISession().setAttribute( "myAtt", "someValue" );
    Thread thread = new Thread( bgRunnable );
    thread.setDaemon( true );
    thread.start();
  }
} );

How to integrate the Eclipse Help System in a RAP application?

Applies to RAP 1.1 and 1.2

An example of how to integrate the Eclipse Help webapplication org.eclipse.help.webapp into a RAP application is provided here File:Org.eclipse.rap.help.integration.zip.

After importing the example project, the following plugins have to be added, since they are not included in the RAP target:

  • javax.servlet.jsp
  • org.apache.commons.el
  • org.apache.commons.logging
  • org.apache.jasper
  • org.apache.lucene.analysis
  • org.apache.lucene
  • org.eclipse.equinox.jsp.jasper.registry
  • org.eclipse.equinox.jsp.jasper
  • org.eclipse.help.appserver
  • org.eclipse.help.base
  • org.eclipse.help.webapp
  • org.eclipse.help

The plug-ins mentioned above should be taken from a "suitable" Eclipse installation. Currently (RAP 1.2) this is version 3.4.

Please note that the included launch configuration must be used. It contains the -Dorg.eclipse.equinox.http.jetty.other.info=org.eclipse.help VM argument. Furthermore, the launch config assumes that the "RAP plug-ins" (...workbench etc.) are in your workspace. The target only contains the "Base plug-ins" (...equinox, ..core.runtime). In case your setup differs, you will need to adjust it.

Applies to RAP 1.3

As RAP 1.3 (starting from M2) delivers the infrastructure for the Help system, some of the facts above need to be adjusted. RAP itself only provides the core infrastructure for (like org.eclipse.help) but without the concrete implementation. This means you still need the plug-ins mentioned above (except org.eclipse.help). In case you don't need a sophisticed help solution (no dynamic help, no interaction with cheatsheets, no context help) the solution above is still sufficient. If you need better help integration (which is normally handled by org.eclipse.help.ui) you need to provide your own help ui contributions. This can be achieved by extending the org.eclipse.ui.helpSupport extension point.

An example of contributing a new helpSupport extension can be found in in this project: File:RapHelpSupport.zip.


The help system customization

The help system customization (like help window title or home page) in a RAP application is simply done by using the org.eclipse.core.runtime.products extension. Even if this extension is not fully supported by RAP, it is necessary to customize the help system. So:

1) add the following in the plugin.xml:

<extension
    point="org.eclipse.core.runtime.products"
    id="help_id">
  <product
      application="dummy"
      description="Integration With Eclipse Help System"
      name="%application.name">
    <property
        name="preferenceCustomization"
        value="product_customization.ini">
    </property>
  </product>
</extension>

2) create the product_customization.ini file in the root folder of my.plugin and add the needed configuration as specified by the product customization guide. For example it could contain:

# Toc ordering.  Ordered list of help TOC's (books) as they would appear
# on the bookshelf. All the other TOCS will be following these books.
org.eclipse.help/baseTOCS=\
  /my.plugin.ui.doc/toc.xml
# help system home page
org.eclipse.help.base/help_home=/my.plugin/doc/help_home.html

3) add the following VM argument to the launch configuration:

-Declipse.product=my.plugin.help_id

Building and deploying as web application

In order to make the help system available when building and deploying the RAP application as a web application, the following setting must be added to the WEB-INF/web.xml:

<init-param>
  <param-name>other.info</param-name>
  <param-value>org.eclipse.help</param-value>
</init-param>

(analogue to setting the -Dorg.eclipse.equinox.http.jetty.other.info=org.eclipse.help in the launch configuration)

In order to add the help system customization when building and deploying the RAP application as a web application, the following setting must be added to the config.ini: eclipse.product=my.plugin.help_id (analogue to the -Declipse.product=my.plugin.help_id in the launch configuration.

To use the help system in Websphere, you have to add another servlet mapping for .jsp files, see https://stackoverflow.com/questions/20972242/eclipse-help-of-rap-2-0-application-does-not-work-with-websphere-8-5-5-0.

How to integrate BIRT?

Please read Integrating BIRT into RAP applications to get started.

How to use expression-based activities for role-based access control?

The expression-based activities can be used to restrict the access to UI elements based on user roles. The following example defines an action demo.xyz.Action1.

  <extension point="org.eclipse.ui.actionSets">
    <actionSet id="demo.actionSet1" label="Admin tools" visible="true">
      <menu id="demo.AdminMenu" label="Admin Menu" path="additions">
        <groupMarker name="groupMarker1"/>
      </menu>
      <action class="demo.Action1" id="demo.xyz.Action1"
        label="Action 1" menubarPath="demo.AdminMenu/groupMarker1"
        style="push">
      </action>
    </actionSet>
  </extension>

Let's suppose that this action should be available only to users with role admin. We will create an expression-based activity demo.Activity1, which will be enabled when the variable demo.roles contains the string admin. Trough the associated pattern binding, the activity will control the availability of the demo.xyz.Action1 action and the corresponding menu item.

  <extension point="org.eclipse.ui.activities">
    <activity id="demo.Activity1" name="Admin menu access control">
      <enabledWhen>
        <with variable="demo.roles">
          <iterate ifEmpty="true" operator="or">
            <equals value="admin">
            </equals>
          </iterate>
        </with>
      </enabledWhen>
    </activity>
    <activityPatternBinding activityId="demo.Activity1"
      pattern="demo/.*xyz.*">
    </activityPatternBinding>
  </extension>
  <extension point="org.eclipse.ui.services">
    <sourceProvider provider="demo.AuthenticationProvider">
      <variable name="demo.roles" priorityLevel="workbench">
      </variable>
    </sourceProvider>
  </extension>

The value of the demo.roles variable is provided by a subclass of AbstractSourceProvider. A dummy implementation is shown below. The actual implementation should obtain the current user roles from a real authentication provider service.

 public class AuthenticationProvider extends AbstractSourceProvider {
 
  public final static String ROLES_VARIABLE = "demo.roles";
 
  public void dispose() {
    //
  }
 
  public Map getCurrentState() {
    // DUMMY CODE! Get the roles from a real authentication service!
    Map result = new HashMap();
    List foo = new ArrayList();
    foo.add( "user" );
    foo.add( "manager" );
    foo.add( "admin" );
    result.put( ROLES_VARIABLE, foo );
    return result;
  }
 
  public String[] getProvidedSourceNames() {
    return new String[]{
      ROLES_VARIABLE
    };
  }
 
  public void updateRights() {
    fireSourceChanged( 0, getCurrentState() );
  }
 }
A small application, which illustrates the outlined approach, can be found here: File:Activities-roles-demo.zip

What is a Session Singleton and how can I implement one?

A "session singleton" is an RWT-specific singleton, which provides access to a unique instance with session scope. This means that in the context of one user session getInstance() will always return the same object, but for different user sessions the returned instances will be different. RWT provides a utility to easily create session singletons, called SingletonUtil. Here's an example of a session singleton:

public class MySessionSingleton {
  private MySessionSingleton() {
    // prevent instantiation from outside
  }
 
  public static MySessionSingleton getInstance() {
    return SingletonUtil.getSessionInstance( MySessionSingleton.class );
  }
  // add other methods ...
}

Note that session singletons can only be directly accessed from the UIThread or with a fake context. When the second parameter "UISession" (introduced in RAP 2.2) is used the thread/context no longer matters.

More on session scope and session singletons can be found in the section on scopes and data stores in RWT in the RAP Developer's Guide.

Note: For RAP < 2.0 use SessionSingletonBase.getInstance(Class)

How to add a Welcome page to a RAP application?

To register a resource that is available at the root URL of the web application, e.g. http://www.example.com/rapdemo/, you can do the following:

  • Register a welcome page resource with an alias:
<extension
      point="org.eclipse.equinox.http.registry.resources">
   <resource
         alias="/welcomepage"
         base-name="/html/index.html">
   </resource>
</extension>
  • Provide a servlet that redirects all GET requests to the welcomepage resource alias:
public class MyRedirectServlet extends HttpServlet {
 
  protected void doGet( HttpServletRequest req, HttpServletResponse resp )
    throws ServletException, IOException
  {
    resp.sendRedirect( resp.encodeRedirectURL( "welcomepage") );
  }
}
  • Register the redirect servlet with the root URL:
<extension
      point="org.eclipse.equinox.http.registry.servlets">
   <servlet
         alias="/"
         class="com.example.rapdemo.MyRedirectServlet">
   </servlet>
</extension>

Note: the redirect servlet is needed here since registering a welcome page resource directly for the "/" alias does not work: a trailing slash would be appended to the resource URL.

I do ... in my bundle activator. Does not work as expected in RAP.

RAP differs from RCP in a way that RCP applications don't have to deal with session scopes but RAP applications have to. In RCP for example you can get access the one and only UI representation instance (your workbench) from every part of the code since application scope and session scope are implicitly the same.

Since you have a multi user environment in RAP (=> multiple Sessions at a time) but only one application wide equinox instance running which is shared between those sessions (application scope) things getting a little bit more complicated. Each session has its own workbench instance since the state of the UIs differ between sessions. So in principle you can easily access the core from the UI, but the other way round isn't possible without additional code.

So what's happening in your code is that you try to access your GUI layer (session scope) from the bundle activator (application scope), which isn't possible as was just explained. Most of the time it is easy to move the code from the activator to the EntryPoint#createUI or IStartup#earlyStartup. Both methods are executed in session scope context, so there won't be any IllegalStateExceptions thrown.

How to add a link to an external URL?

Since RAP 1.5, you can use simple HTML markup in a couple of widgets, including Label:

Label label = new Label( page, SWT.NONE );
label.setData( RWT.MARKUP_ENABLED, Boolean.TRUE );
label.setText( "<a href=\"http://xkcd.com/\" target=\"_blank\">Plain Link</a>" );

Alternatively, you can use a browser widget that contains the link:

Browser browser = new Browser( parent, SWT.NONE );
browser.setText( "<a href=\"http://xkcd.com/\" target=\"_blank\">Plain Link</a>" );

A third option is to open an external browser window programmatically. However, this will be prevented by popup blockers.

Button button = new Button( page, SWT.PUSH );
button.setText( "External Browser" );
button.addSelectionListener( new SelectionAdapter() {
  @Override
  public void widgetSelected( SelectionEvent e ) {
    int browserStyle = ExternalBrowser.LOCATION_BAR | ExternalBrowser.NAVIGATION_BAR;
    ExternalBrowser.open( "google", "http://xkcd.com/", browserStyle );
  }
} );

In RAP 2.0 or later, use the URLLauncher instead of ExternalBrowser.

How to provide a download link?

To provide download functionality in a RAP (or pure RWT) application, you need two components:

  • a download link (see above)
  • a service handler (implementation of ServiceHandler) to provide the download content

Below is an example of a very basic downloads service handler. The request has a parameter "filename" which indicates which content (either static or dynamically generated) should be sent to the client. The MyDataStore will known how to find the required content. In our example the content is retrieved as byte[] but InputStreams and Readers can be easily accommodated as well.

public class DownloadServiceHandler implements ServiceHandler {
 
  public void service( HttpServletRequest request, HttpServletResponse response )
    throws IOException, ServletException
  {
    // Which file to download?
    String fileName = request.getParameter( "filename" );
    // Get the file content
    byte[] download = MyDataStore.getByteArrayData( fileName );
    // Send the file in the response    
    response.setContentType( "application/octet-stream" );
    response.setContentLength( download.length );
    String contentDisposition = "attachment; filename=\"" + fileName + "\"";
    response.setHeader( "Content-Disposition", contentDisposition );
    response.getOutputStream().write( download );
  }
}

The service handler has to be registered:

ServiceManager manager = RWT.getServiceManager();
ServiceHandler handler = new DownloadServiceHandler();
manager.registerServiceHandler( "downloadServiceHandler", handler );

The download link has to point to a URL created as follows:

private String createDownloadUrl( String filename ) {
  StringBuilder url = new StringBuilder();
  url.append( RWT.getServiceManager().getServiceHandlerUrl( "downloadServiceHandler" ) );
  url.append( '&' ).append( "filename" ).append( '=' ).append( filename );
  return url.toString();
}

How to switch locale/language on user action?

Switching the locale in a running session isn't supported out of the box. So currently you'll have to restart the session to be able to switch the locale.

To restart the session, we will send some Javascript code, which changes the parent.window.location.href DOM attribute of the current page. The new parent.window.location.href value is the URL of the current page plus a new parameter which contains the selected locale code.

Restarting the session and passing the new locale as a parameter

The changeLocale method is invoked when the user requests a language change (e.g. from a selection listener attached to a button). In order to add the page-reloading Javascript code to the HTTP response, we register a life-cycle PhaseListener:

void changeLocale( String locale ) {
  final String url = createUrl( locale  );
  // Uses a non-public API, see http://bugs.eclipse.org/342995
  JSExecutor.executeJS( "parent.window.location.href=\"" + url + "\";" );
}
 
private String createUrl( String locale ) {
  StringBuffer url = new StringBuffer();
  url.append( RWT.getRequest().getContextPath() );
  url.append( RWT.getRequest().getServletPath() );  
  url.append( "?locale=" );
  url.append( locale );
  return RWT.getResponse().encodeURL( url.toString() );
}

Getting the locale from the URL parameter

Before creating the UI, set the locale to the value retrieved from the URL parameter:

public int createUI() {
  String locale = RWT.getRequest().getParameter( "locale" );
  if( locale != null ) {
    RWT.setLocale( new Locale( locale ) );
  }
  // Create the UI
  ...
}

How to display dynamically created images?

  • Create an instance of Browser widget to place the image in the UI
  • Create an image, e.g. using java.awt.Graphics2D
  • Use a custom service handler to deliver the image.

Create a Browser widget to render the image

To place the image in the application UI, we use a Browser widget with a single HTML img tag. The URL in the src attribute contains a key, used to identify the generated image.

 
  private final static String SERVICE_HANDLER = "imageServiceHandler";
  private final static String IMAGE_KEY = "imageKey";
 
    ...
    // register the service handler
    RWT.getServiceManager().registerServiceHandler( SERVICE_HANDLER,
                                                    new ImageServiceHandler() );
    // create a Browser to display the image
    Browser browser = new Browser( shell, SWT.NONE );;
    // create the image
    BufferedImage image = createImage();
    // store the image in the UISession for the service handler
    RWT.getUISession().setAttribute( IMAGE_KEY, image );
    // create the HTML with a single <img> tag.
    browser.setText( createHtml( IMAGE_KEY ) );
    //
    ...
 
  private String createHtml( String key ) {
    StringBuffer html = new StringBuffer();
    html.append( "<img src=\"" );
    html.append( createImageUrl( key ) );
    html.append( "\"/>" );
    return html.toString();
  }
 
  private String createImageUrl( String key ) {
    StringBuffer url = new StringBuffer();
    url.append( RWT.getServiceManager().getServiceHandlerUrl( SERVICE_HANDLER ) );
    url.append( "&imageId=" );
    url.append( key );
    url.append( "&nocache=" );
    url.append( System.currentTimeMillis() );
    return url;
  }
}

Create the image

Use java.awt.Graphics2D as a canvas on which to draw the image. The server, where the application will be deployed, most probably has no graphics UI system installed. Therefore we need to run AWT in "headless mode" by setting the system property java.awt.headless to true.

In the example below, the produced BufferedImage is placed in the UISession and the key is used to generate the new <img> tag for the Browser.

private BufferedImage createImage() {
  BufferedImage image = new BufferedImage( 200, 200, BufferedImage.TYPE_INT_ARGB );
  Graphics2D gr2d = image.createGraphics();
  // draw the image
  gr2d.setColor( new Color( 0, 0, 255 ) );
  gr2d.drawRect( 0, 0, widht - 1, height - 1 );
  ...
  return image;
}

Service Handler

The service handler has to be registered with RWT.getServiceManager().registerServiceHandler() - see the first code snippet above. The handler receives the image key as a request parameter, and uses it to retrieve the image from the UISession.

public class ImageServiceHandler implements ServiceHandler {
  public void service( HttpServletRequest request, HttpServletResponse response )
    throws IOException, ServletException
  {
    String id = request.getParameter( "imageId" );
    BufferedImage image = ( BufferedImage )RWT.getUISession().getAttribute( id );
    response.setContentType( "image/png" );
    ServletOutputStream out = response.getOutputStream();
    ImageIO.write( image, "png", out );
  }
}

How to deliver session-scoped resources

Often a RWT application UI includes session specific resources (e.g. images) - either because they are dynamically created or specific credentials are required to access them. This use case can be handled by a custom service handler and a Browser widget (or a custom widget). Please see the FAQ article #How_to_display_dynamically_created_images.3F for the implementation details.


How to access a RAP application without specifying a servlet name?

Let's suppose that we want to simplify the URL of our RAP application and be able to access it with http://www.example.org/webapp instead of http://www.example.org/webapp/servlet_path.

The recommended solution is to use a redirection servlet:

  • Write a servlet which redirects requests for the web application root to your application servlet path:
  public class RedirectServlet extends HttpServlet {
 
  protected void doGet( HttpServletRequest request,
                        HttpServletResponse response )
    throws ServletException, IOException
  {
    redirect( request, response );
  }
 
  protected void doPost( HttpServletRequest request,
                         HttpServletResponse response )
    throws ServletException, IOException
  {
    redirect( request, response );
  }
 
  static void redirect( HttpServletRequest request, 
                        HttpServletResponse response )
    throws IOException
  {
    if( request.getPathInfo().equals( "/" ) ) {
      response.sendRedirect( response.encodeRedirectURL( "servlet_path" ) );
    } else {
      response.sendError( HttpServletResponse.SC_NOT_FOUND );
    }
  }
}
  • Register the redirect servlet with alias "/":
<extension
      point="org.eclipse.equinox.http.registry.servlets">
   <servlet
         alias="/"
         class="org.example.RedirectServlet">
   </servlet>
</extension>

Note: Make sure that the servlet redirects only requests for "/" and returns 404 for all others. Failing to do so might create redirect loops.

How can I use Jetty basic authentication in my application?

Assuming you are running your RAP application with Jetty, it is possible to setup the basic authentication mechanism that will cause the browser to ask the user for a username and password:

  • First create a class that extends JettyCustomizer. This will allow you to customize the Jetty context to setup the basic authentication mechanism. Here is a sample class:
public class BasicAuthCustomizer extends JettyCustomizer { 
 
  public Object customizeContext( Object o, Dictionary dictionary ) {
    Constraint constraint = new Constraint();
    constraint.setName( Constraint.__BASIC_AUTH );
    constraint.setRoles( new String[]{ "admin" } );
    constraint.setAuthenticate( true );
 
    ConstraintMapping mapping = new ConstraintMapping();
    mapping.setConstraint( constraint );
    mapping.setPathSpec( "/*" );
 
    HashUserRealm realm = new HashUserRealm( "MyRealm" );
    .. // fill username, password and roles if needed
 
    SecurityHandler securityHandler = new SecurityHandler();
    securityHandler.setUserRealm( realm );
    securityHandler.setConstraintMappings( new ConstraintMapping[]{ mapping } );
 
    Context context = ( Context )o;
    context.setSecurityHandler( securityHandler );
 
    return o;
  }
 
}
  • Specify the fully qualified name of this class in your start script. For example:
-Dorg.eclipse.equinox.http.jetty.customizer.class=com.mycompany.console.jetty.BasicAuthCustomizer
  • In order for this class to be loaded correctly by the Eclipse code that customizes Jetty, you will need to put this class into a fragment that attaches to the org.eclipse.equinox.http.jetty bundle.

How to register a file/folder as resource?

Arbitrary resources can be registered via the extension point "org.eclipse.equinox.http.registry.resources".

Example:
<extension
      point="org.eclipse.equinox.http.registry.resources">
   <resource
         alias="/"
         base-name="resources">
   </resource>
</extension>

The properties are used the following way:

  • base-name: The path which is to be mapped (project folder is root).
  • alias: Path the base-name is mapped to on the server.

In the example above the folder ".../<project_name>/resources" is mapped to the server root http://<url_to_rap_server>/.

How to specify context path when using the RAP launcher?

Since RAP Tools 1.5 the context path could be specified in RAP launcher "Main" tab.

Note: The solution below only applies only to earlier versions.

When starting an application with the RAP launcher (which is typical during the development), the URL of the RAP servlet is

 http://host:port/<servlet name>?startup=<entry point parameter>


If the same application is deployed in a standalone application server, usually a non-empty context path will be added to the URL:

 http://host:port/<context path>/<servlet name>?startup=<entry point parameter>


Sometimes it is more convenient to have the same URLs (i.e. the same context paths) in both deployment scenarios. Typically, the context path for a web application is assigned by the system administrator when the application is deployed or is inferred from the war file name.

For the embedded Jetty servlet container that is used by the RAP launcher, the context path can be specified as a VM parameter in the launch configuration:

 -Dorg.eclipse.equinox.http.jetty.context.path=<context path>


In the launch configuration dialog, you should also change the Servlet name if the Open application in ... is checked.

Here is a complete example:

  • RAP launcher configuiration

 VM argument: -Dorg.eclipse.equinox.http.jetty.context.path=/mycontext 
 Servlet name: mycontext/rap
 Entry point: default


  • Application URL

 http://127.0.0.1:<port>/mycontext/rap?startup=default

How to update the UI from a background thread using Jobs?

Applications that wish to call UI code from a non-UI thread must provide a Runnable that calls the UI code. The methods syncExec(Runnable) and asyncExec(Runnable) in the Display class are used to execute these runnables in the UI thread during the event loop.

It is good practice to check if your widget is disposed from within the runnable when using Display#asyncExec(). Since other things can happen in the UI thread between the call to asyncExec and the execution of your runnable, you can never be sure what state your widgets are in by the time your runnable executes.

It's a fairly typical situation when a widget (e.g. Table) is populated with data retrieved from a database or some other external data source. The data source access code should not be ran in the UI thread in order to keep the UI responsive. The org.eclipse.core.runtime.jobs package provides an infrastructure for running concurrent operations (jobs) in a background threads.

In the following example a job is created to fetch some data from a mocked-up data store and to show the result in a Table. Since the Job runs in the background, the update of the TableViewer will be made from a Runnable executed via Display#asyncExec().

Job job = new Job( "Fetch Job" ) {
  protected IStatus run( IProgressMonitor monitor ) {
    // ... fetch data from "slow" data store
    final Object[] result = DataStore.getData();
    display.asyncExec( new Runnable() {
      public void run() {
        updateTable( result );
      }
    } );
    return Status.OK_STATUS;
  }
};
job.schedule();
 
...
 
public void updateTable( Object[] data ) {
  if( !tableViewer.getTable().isDisposed() ) {
    tableViewer.setInput( data );
    tableViewer.refresh();
  }
}

When using Jobs, the UI updates are pushed automatically to the client because the IProgressMonitor takes care of activating the UICallBack mechanism. The article #How_to_update_the_UI_from_a_background_thread.3F shows how to achieve similar functionality in a standalone RWT application.

How to update the UI from a background thread

Note: Since RAP 2.0 UICallBack has been replaced with ServerPushSession. Use ServerPushSession#start() and ServerPushSession#stop() to activate/deactivate it.

To allow automatic UI-updates by server side background threads the UI Callback needs to be activated. Call UICallBack#activate before the start of a thread and UICallBack#deactivate at the end. Each activation needs a session unique identifier as a kind of reference pointer to be able to decide when all background threads are finished.

The following steps outline the pattern:

  • activate the callback mechanism (must be done in the UI thread)
  • start background work
  • update the UI
  • deactivate the UI call-back.

The UI callback is activated by calling UICallBack.activate( "callback id" ), where the "callback id" is a session-unique identifier to trace the activation and deactivation. A typical use case is sketched below:

UICallBack.activate( "callback id" );
Thread bgThread = new Thread( bgRunnable );
bgThread.setDaemon( true );
bgThread.start();

The actual changes to the UI must occur in the UI thread and are scheduled from the background thread by calling display.asyncExec(). As always is the case when updating UI from a background thread, we must check if the UI widget is not already disposed before using it. After the background work is done, we will disable the UI callback to free the aquired resources:

Runnable bgRunnable = new Runnable() {
  public void run() {
    // do some work...
    // schedule the UI update
    display.asyncExec( new Runnable() {
      public void run() {
        if( !widget.isDisposed() ) {
          // update the UI
        }
      }
    } );
    // Deactivate the UI call-back
    display.asyncExec( new Runnable() {
      public void run() {
        UICallBack.deactivate( "callback id" );
      }
    } );
  }
};

Technically, the UI callback is based on the long-polling technique. From the moment the UICallBack.activate() is called, the JavaScript client makes sure that there is always a pending request, waiting at the server to be processed. See the article Server push in RWT (aka UI Callback) for more details

Note: if you are using the workbech APIs you may consider to update the UI using Jobs.

How do I get notified when the browser window/tab is closed?

In most recent browsers, the UI session is automatically destroyed when the client's browser window/tab is closed. For older browsers, the HTTP session timeout is used to detect whether the user has left the application.

When UI session is destroyed, all objects that are held by it (e.g. the Workbench, the Display) will be released.

There are several ways to do some clean up before the UI session is destroyed. The first choice in most cases is to to register a UISessionListener:

RWT.getUISession().addUISessionListener( new UISessionListener() {
  public void beforeDestroy( UISessionEvent event ) {
    // Perform cleanup        
  }
} );
An alternative way is to attach an SWT.Close listener to the Display or register a Runnable with disposeExec():
display.addListener( SWT.Close, new Listener() {
  public void handleEvent( Event event ) {
    // Perform cleanup        
  }
} );
 
display.disposeExec( new Runnable() {
  public void run() {
    // Perform cleanup
  }
} );

Note that when using the RAP launcher, by default a HTTP session never expires. To change the timeout value, adjust the setting on the "Main" tab.

The timeout value can also be set on a per session basis:

RWT.getUISession().getHttpSession().setMaxInactiveInterval(<timeout in Seconds>);

How do I implement a key binding that triggers a Command?

Since 1.4 M5, RAP supports the key bindings extension point (see bug bug 282449 for details).

Since 1.4 M7, RAP supports key bindings in plain RWT applications by attaching a list of active key bindings to the display using Display.setData with RWT.ACTIVE_KEYS as key (see bug bug 343248). For details, please refer to the JavaDoc of RWT.ACTIVE_KEYS.

Note: The solution below only applies only to earlier versions.

First, you need to register some Javascript code to attach a key event listener to the qooxdoo ClientDocument. In this example, the key listener checks for the key sequence Ctrl+1 and sends a request with a custom request parameter if these keys are pressed.

String jsCode = ""
  + "var doc = qx.ui.core.ClientDocument.getInstance();\n"
  + "doc.addEventListener( \"keydown\", function( evt ) {\n"
  + "  var req = org.eclipse.swt.Request.getInstance();\n"
  + "  if ( evt.isCtrlPressed() && evt.getKeyIdentifier() ==\"1\") {\n"
  + "    req.addParameter( \""+ PARAMETER_KEY_PRESSED +"\", \"true\" );\n"
  + "    req.send();\n"
  + "  }\n"
  + "} );\n";
JSExecutor.executeJS( jsCode );

A PhaseListener is registered that scans the request for the parameter and triggers the command if it was found:

executeCommandPhaseListener = new ExecuteCommandPhaseListener( "my.command.id", PARAMETER_KEY_PRESSED );
RWT.getLifeCycle().addPhaseListener( executeCommandPhaseListener );

with executeCommandPhaseListener being an instance of the following class:

public class ExecuteCommandPhaseListener implements PhaseListener {
 
  private final String commandId;
  private final String parameter;
 
  public ExecuteCommandPhaseListener( String commandId, String parameter ) {
    this.commandId = commandId;
    this.parameter = parameter;
  }
 
  public PhaseId getPhaseId() {
    return PhaseId.READ_DATA;
  }
 
  public void beforePhase( final PhaseEvent event ) {
    // do nothing
  }
 
  public void afterPhase( final PhaseEvent event ) {
    HttpServletRequest request = RWT.getRequest();
    String value = request.getParameter( parameter );
    if( "true".equals( value ) ) {
      ProcessActionRunner.add( new Runnable() {
 
        @Override
        public void run() {
          IHandlerService service
            = (IHandlerService)PlatformUI.getWorkbench().getService( IHandlerService.class );
          try {
            service.executeCommand( commandId, null );
          } catch( CommandException e ) {
            throw new RuntimeException( e );
          }
        }
      } );
    }
  }
}

That's it. The Command with the id my.command.id is triggered when the registered key event sent the parameter PARAMETER_KEY_PRESSED with the value "true". As an improvement, you could also listen for different key sequences to trigger different commands.

How to integrate Equinox Security/JAAS in RAP?

Please read the Equinox Security Integration page

How to run a RAP application in multiple browser tabs/windows?

Since RAP 2.1, multiple browser tabs are supported without any modifications (see bug 390236 for details). The following discussion is still valid for RAP versions prior to 2.1.

By default, servlet engines use cookies to identify a session. Browsers usually re-use the same session cookie for all tabs and windows within the same process. In the end, the RAP servlet cannot distinguish request that come from the same browser/client-session and rejects request that come from a second tab or window with the message "Multiple browser-instances or browser-tabs per session are not supported. You may click OK for restarting the session.". For a complete history of this issue, please see bug 285398.

The solution is to disable the use of cookies for session-identification of the servlet engine. For instruction on how to configure Tomcat and Jetty, read the sections below.

Tomcat

(tested with version 5.5 and 6.0) In the META-INF folder of your web application, place a context.xml file with the following content:
<Context cookies="false">
</Context>
The cookies=false attribute causes Tomcat to not use cookies to identify a session, and rely only on URL rewriting by the application (RAP in this case).

Upon deployment of the WAR, the context.xml will be copied to an appropriate place under the conf folder. Once this file exists, it will not be replaced if a new WAR with a newer META-INF/context.xml is deployed. For further implications and more detailed information, please read the Apache Tomcat Configuration Reference

The RAP Examples Demo uses the technique described here and can be viewed online and downloaded.

Jetty

This applies only to running RAP with standalone Equinox using the Jetty-based HttpService (e.g. launching a RAP application from within the IDE).

To enable tabbed browsing with Jetty, a so-called JettyCustomizer is needed. The org.eclipse.rap.jettycustomizer fragment bundle contains an implementation that instructs Jetty to not use cookies to identify a session, and rely only on URL rewriting by the application (RAP in this case).

For the customizer to be picked up when Jetty is started, the following system property must be specified: -Dorg.eclipse.equinox.http.jetty.customizer.class=org.eclipse.rap.jettycustomizer.internal.SessionCookieCustomizer

Currently, the fragment can be obtained from the 2.1 branch in the RAP git repository.

How can i speed-up drawing on the GC?

The RAP GC-implementation is only suited for relatively simple or static graphs. This is especially true for Internet Explorer which slows down significantly after about 200 drawing operations. Other browser can manage much more (depending on the clients hardware-resources), but will still need a very noticeable amount of time after a couple of hundred operations. Currently the only way to speed-up drawing is to reduce the number of drawing-operations as much as possible:

  • Consider removing purely decorative elements.
  • If possible, use drawPolyLine instead of multiple drawLine operations.
  • If you have areas in your graph that change independently from each other, use one canvas for each of these areas instead of one big canvas.
  • If you have completely static elements, you can also set a background-image on the canvas and draw the dynamic elements above this image.

In future RAP-releases the performance might improve, but it will probably never be as fast as in SWT.

Note: Since RAP 2.1 Path API is supported, which optimizes shapes drawing.

On my Tree or Table the item or cell background is not drawn when the row is hovered or selected.

This is the default behavior for all RAP versions until 1.4, and can not be changed. For RAP 1.5 this is still the default, but can be changed by theming. The default (or buisness) theme uses the TreeRow-Overlay/TableRow-Overlay elements to render the hover and selection state. These are painted over the item itself.

To change this, make a theme contribution are custom theme that looks like this:

/* First, overwrite the overlay to be always transparent. */
Table-RowOverlay,
Table-RowOverlay:hover, 
Table-RowOverlay:selected, 
TableItem:selected:unfocused,
TableItem:linesvisible:even:hover,
TableItem:linesvisible:even:selected,
TableItem:linesvisible:even:selected,
TableItem:linesvisible:even:selected:unfocused {
  background-color: transparent;
  color: inherit;
  background-image: none;
}
 
/* Now use the item instead of the overlay to create the hover and selection effects. */
 
TableItem:hover {
  color: #4a4a4a;
  background-color: #b5b5b5;
  background-image: gradient(
    linear, left top, left bottom,
    from( #ebebeb ),
    to( #d5d5d5 )
  );
}
 
TableItem:selected {
  color: #ffffff;
  background-color: #5882b5;
  background-image: gradient(
    linear, left top, left bottom,
    from( #5882b5 ),
    to( #416693 )
  );
}
 
TableItem:selected:unfocused {
  color: #ffffff;
  background-color: #b5b5b5;
  background-image: gradient(
    linear, left top, left bottom,
    from( #b5b5b5 ),
    to( #818181 )
  );
}
 
TableItem:linesvisible:even:hover {
  color: #4a4a4a;
  background-color: #b5b5b5;
  background-image: gradient(
    linear, left top, left bottom,
    from( #ebebeb ),
    to( #d5d5d5 )
  );
}
 
TableItem:linesvisible:even:selected {
  color: #ffffff;
  background-color: #5882b5;
  background-image: gradient(
    linear, left top, left bottom,
    from( #5882b5 ),
    to( #416693 )
  );
}
 
TableItem:linesvisible:even:selected:unfocused {
  background-color: #959595;
  background-image: gradient(
    linear, left top, left bottom,
    from( #b5b5b5 ),
    to( #818181 )
  );
  color: #ffffff;
}

Tree is the same, just with TreeItem and TreeRow-Overlay. You can also mix and use overlay only for selection, but item for hover. In that case selection overwrites the item colors, but not the hover.

On my Tree or Table the selection or hover effect is hidden on items with background color.

This happens if you make a custom theme that does not use the TableRow-Overlay/TreeRow-Overlay element with RAP 1.5. In your custom theme, replace all occurrence of TableItem/TreeItem that use selection/hover states with TableRow-Overlay/TreeRow-Overlay.

How to make RAP applications Accessible

RAP does not support the SWT accessibility API since it is not suitable for web applications. To be able to fulfill the Web Content Accessibility Guidelines you will need to add ARIA attributes to your application. You can do this (within limitations) using the web client API (RAP 2.3+ only, see Developers Guide), or - more thoroughly and comfortably - with the Aria-Addon. Please also read this blog post on the topic.

How to create UI Tests for RAP

Creating UI-Tests for RAP application is a challenge because nearly all widgets consist only of DIV elements with no stable IDs, making them difficult to identify. As of RAP 2.3, developer can set HTML attributes on any widget, making it easy to identifiy specific widget instances. Using the Aria-Addon (almost) all widgets automatically reflect their type and state in HTML attributes. Please also read this blog post on the topic.


Deployment

Problems using Import-Package header

Since RAP 1.1 the workbench introduced a concept of so-called "split packages". This is a way of OSGi to have virtual packages which are physically split across several bundles. See this post and the OSGi 4.1 specification (§3.13.3) for more information about split packages.

A common problem with them is that a split-package is only resolved when there is at least one additional split part available during runtime. As the RAP infrastrcuture only contains the workbench bundle at the moment this constrain is not met. In RCP world you often have the org.eclipse.ui.ide bundle available which contributes to these split packages and let Equinox resolve the constrains.

In order to work around this problem you are able to import only a certain part of the package (the part of the workbench in our example). You just need to extend your Import-Package declaration with the split attribute as shown in the following MANIFEST.MF fragment:

Manifest-Version: 1.0
...
Import-Package: org.eclipse.ui; ui.workbench="split",
org.eclipse.ui.part; ui.workbench="split"

The ui.workbench="split" directive tells Equinox to use only the "ui.workbench" part of this split package.

Exported WAR file does not work

The application is working at development time, but when after exporting it, the web application does not work. Check the following:

  • Check your build.properties
    • are you exporting the plugin.xml?
    • if you are using plugin.properties files, make sure they are exported
    • are all libraries you are using listed on the plug-ins class path?
    • Tip: As PDE build sometimes swallows error messages try exporting your feature with "Deployable Feature" export, this may turn up error messages
  • Enable the OSGi console by adding this init-param to the web.xml:
<init-param>
  <param-name>commandline</param-name>
  <param-value>-console</param-value> 
</init-param>
    • you may want to add a port after -console in unix environments, you can then telnet to the OSGi console
    • type ss in the console and see if all bundles are started. If not try starting them with "start <bundle-id>". The stack traces may hint to what is missing
    • make sure that there is a org.eclipse.equinox.servletbridge.extensionbundle in the ss-listing whos state is RESOVLED
  • Make sure that the WAR does not contain the javax.servlet bundle because it is provided by the hosting servlet container. If your code depends on classes from javax.servlet, hte dependency must be expressed as an Import-Package. Do not list javax.servlet as a Require-Bundle.
  • Make sure that the WAR does not contain the org.eclipse.update.configurator bundle.
  • The WAR contains compile errors. You will probably find a zip archive somewhere in your output folder. It contains log files with the compiler output.
  • Start with a working example: rapdemo.war and integrate your plug-ins.
  • Use the product export to validate your feature:
    • Create a product configuration with an arbitrary name (File > New > Other > Product Configuration)
    • Select "The product configuration is based on features" on the "Overview" page
    • Add your feature to the list of features on the "Dependencies" page
    • Press the Validate' button in the top-left corner of the editor. Ignore complaints about missing javax.servlet packages. Be aware that unresolved optional dependencies aren't reported as errors.
  • If you are re-deploying, make sure to delete the work directory of your servlet engine (e.g. <tomcat_install>/work/Catalina/localhost/work/<webapp_name> in Tomcat)

How do I make remote calls to EJB's deployed on Jboss 4.x AS

This is not a solution for calling local interface EJB's (I wasn't able to do it :( ), neither for EJB3 specification (didn't tried yet)

Nevertheless is more useful to make remote calls as the RAP application and the JBoss AS could be on separate machines (or will be sometime in the application life-cycle)

  • prepare a jar with EJB's interfaces which you are about to call from RAP (I think here you may use it wrapped as a bundle if you want to separate it from your application bundle)
  • put the jar in the application runtime classpath or add it as a dependency plugin if you use it as a bundle
  • add the following jar's from the jboss installation folder in the application runtime classpath : jboss.jar, jboss-remoting.jar, jboss-serialization.jar, jbosssx.jar, jboss-transaction.jar, jnpserver.jar

Here's an example code for calling the EJB's remote interface :

Hashtable<String, String> props = new Hashtable<String, String>();
props.put( Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory" );
props.put( Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces" );
props.put( Context.PROVIDER_URL, "jnp://localhost:1099" ); // here you put your JBoss AS server's address
Context ctx = null;
try {
  ctx = new InitialContext( props );
} catch( NamingException e ) {
  throw new RuntimeException( "fail to get initial context", e );
}
Object obj = null;
try {
  obj = ctx.lookup( "Test" ); //The jndi ejbs name is Test
} catch( NamingException e ) {
  throw new RuntimeException( "could not obtain test home interface", e );
}
TestHome home = ( TestHome )PortableRemoteObject.narrow( obj, TestHome.class );
try {
  testService = home.create();
} catch( CreateException e ) {
  throw new RuntimeException( "could not create test remote interface", e );			
} catch( RemoteException e ) {
  throw new RuntimeException( "remote exception when creating test remote interface", e );
}
try {
  testService.callSomeBusinessMethod(...);
} catch( RemoteException e ) {
  throw new RuntimeException( "remote exception when calling business method", e );
}

How do I develop an RWT standalone application with RAP <= 1.4

To use the RWT standalone application in Tomcat follow the steps below:

  • Create a new Java project.
  • In the project create the folders:
    • WEB-INF
    • WEB-INF/lib
    • WEB-INF/classes
  • In the WEB-INF folder, place a file named web.xml with the content below:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">
 
  <display-name>Welcome to RWT</display-name>
  <description>Welcome to RWT</description>
 
  <context-param>
    <param-name>org.eclipse.rwt.entryPoints</param-name>
    <param-value>com.example.HelloWorld</param-value>
  </context-param>
 
  <listener>
    <listener-class>org.eclipse.rwt.internal.engine.RWTServletContextListener</listener-class>
  </listener>
 
  <servlet>
    <servlet-name>rwtServlet</servlet-name>
    <servlet-class>org.eclipse.rwt.internal.engine.RWTDelegate</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>rwtServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
  • You can use several features (e.g. branding, theming) that are usually specified with extension points by using context parameters.
This is optional:
<context-param>
  <param-name>org.eclipse.rwt.themes</param-name>
  <param-value>useradmin#theme/theme.css</param-value>
</context-param>
 
<context-param>
  <param-name>org.eclipse.rwt.brandings</param-name>
  <param-value>rap.RAPUseradminBranding</param-value>
</context-param>
  • Copy the following jars from RAP in the WEB-INF/lib folder:
    • org.eclipse.rap.rwt
    • org.eclipse.rap.q07 (only relevant for RAP <= 1.4)
  • In case you want to make use of JFace, you also need to add these jars
    • org.eclipse.core.commands
    • org.eclipse.equinox.common
    • org.eclipse.rap.jface
  • Add these jars to the project build path as external jars.
  • Change the projects' output folder to WEB-INF/classes.
  • Use a normal implementation of IEntryPoint#createUI() - HelloWorld.java:
package com.example;
 
import org.eclipse.rwt.lifecycle.IEntryPoint;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
 
public class HelloWorld implements IEntryPoint {
  public int createUI() {       
    Display display = new Display();
    Shell shell = new Shell( display );
    Label label = new Label( shell, SWT.NONE );
    label.setText( "Hello RAP World" );
    label.setSize( 80, 20 );
    shell.setSize( 500, 400 );        
    shell.open();
    while( !shell.isDisposed() ) {
      if( !display.readAndDispatch() )
        display.sleep();
    }
    display.dispose();
    return 0;
  }
}
  • ZIP the the WEB-INF folder (including the folder itself) as RWT_Standalone.war.
  • Deploy the RWT_Standalone.war in Tomcat.
  • Start the standalone RWT application from:
http://<host>:<port>/RWT_Standalone/main
  • If you get an IllegalArgumentException or a ClassNotFoundException after deploying your application, please check first the contents of your WAR file. Maybe it doesn't contain all class files.

How do I develop an RWT standalone application with RAP >= 1.5

It is recommended to use the RAP Tooling for developing RWT applications. Though it is not stricly necessary to use the tooling, it eases development with a launch configuration tailored for RWT applications and documentation.

Follow the steps outlined below and you will have a simple web application up and running in a few minutes.

  • Create a Java Project (or a plug-in project if you prefer and if you are familiar with plug-in development)
  • Configure the project to match the layout of a web application. You may skip or postpone this step if you are using RAP Tooling to launch the application. The layout is neccessary if you want to deploy the project as a WAR.
    • Create the three folders: WEB-INF, WEB-INF/lib, WEB-INF/classes
    • Change the projects' output folder to WEB-INF/classes.
  • Copy the org.eclipse.rap.rwt_* jar from the RAP Runtime into the WEB-INF/lib folder and add it to the projects' build path. The org.eclipse.rap.rwt.source_* jar contains the RWT source code. To be able to browse the sources and read JavaDoc, specify this jar as the Source Attachment
  • Implement an IEntryPoint like below:
public class HelloWorld implements IEntryPoint {
  public int createUI() {
    Display display = new Display();
    Shell shell = new Shell( display );
    shell.setLayout( new GridLayout() );
    Label label = new Label( shell, SWT.NONE );
    label.setText( "Hello RAP World" );
    shell.setSize( 500, 400 );
    shell.open();
    return 0;
  }
}

With the RAP Tooling installed, you can already launch your HelloWorld application. To do so, select the HelloWorld class (i.e. in the Package Explorer) and choose Run As > RWT Application from the context menu.

If you whish to deploy your application on an external servlet engine, or if you need a deployment descriptor for other reasons, or if you haven't installed the RAP Tooling, a few more steps are required to run the application.

  • Place a deployment descriptor (web.xml) in the WEB-INF folder with the content below:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">
 
  <context-param>
    <param-name>org.eclipse.rap.applicationConfiguration</param-name>
    <param-value>com.example.HelloWorldConfiguration</param-value>
  </context-param>
 
  <listener>
    <!-- For RAP 2.x, use org.eclipse.rap.rwt.engine.RWTServletContextListener instead -->
    <listener-class>org.eclipse.rwt.engine.RWTServletContextListener</listener-class>
  </listener>
 
  <servlet>
    <servlet-name>rwtServlet</servlet-name>
    <!-- For RAP 2.x, use org.eclipse.rap.rwt.engine.RWTServlet instead -->
    <servlet-class>org.eclipse.rwt.engine.RWTServlet</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>rwtServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>
  • Provide an implementation of ApplicationConfigurator to configure your application like shown below
public class HelloWorldConfiguration implements ApplicationConfiguration {
 
  @Override
  public void configure( Application application ) {
    Map<String, String> properties = new HashMap<String, String>();
    properties.put( WebClient.PAGE_TITLE, "Hello world" );
    application.addEntryPoint( "/hello", HelloWorld.class, properties );
  }
 
}
  • Again you can use the RAP Tooling to launch the application from the just created web.xml. To do so, create a new RWT Launch Configuration and select "Run from web.xml". Enter the location of the web.xml file and specify "hello" as the servlet path.

You may also find the JFace components useful. In order to use them from RWT standalone, you will need to add the following jars from the RAP Runtime:

  • org.eclipse.rap.jface
  • org.eclipse.core.runtime
  • org.eclipse.core.commands
  • org.eclipse.equinox.common

Why are there encoding issues after deploying my RAP application as a WAR?

The pde.exportFeatures Ant task uses the encoding specified in file.encoding system property, which might not match the encoding of your source files. You can change this behaviour by explicitly defining the encoding used in pde.exportFeatures task. Just identify the plugin, which contains the strings with non-ASCII characters, and add javacDefaultEncoding property to his build.properties file.

The following example sets the encoding to utf-8, but you should specify whatever encoding your source files are using:

  javacDefaultEncoding.. = UTF-8

Reference: Feature and Plug-in Build Configuration Properties in the Plug-in Development Environment Guide.

I cannot access resource/image after deploying as a WAR

The image/resource is visible when running from within the IDE but is missing after deploying.

All resources must be explicitly listed in the bin.includes variable or they will not be included in the binary distribution of your plug-in. Please verify your build.properties files to ensure that your plug-ins are not missing any entries for images and other resource files.

How do I stress test or load test my RAP application

See the article Load testing / stress testing of RAP applications

How can I test my application with HTTPS?

When running a RAP application from the IDE, the Jetty servlet container is used. Jetty can simply be put into HTTPS mode:

  • First you need to create a certificate using the keytool from the Java SDK:
# keytool -keystore keystore -alias jetty -genkey -keyalg RSA
Enter keystore password:  password
Re-enter new password:  password
What is your first and last name?
  [Unknown]:  RAP Example
What is the name of your organizational unit?
  [Unknown]:  RAP Team
What is the name of your organization?
  [Unknown]:  Eclipse
What is the name of your City or Locality?
  [Unknown]:  
What is the name of your State or Province?
  [Unknown]:  
What is the two-letter country code for this unit?
  [Unknown]:  
Is CN=RAP Example, OU=RAP Team, O=Eclipse, L=Unknown, ST=Unknown, C=Unknown correct?
  [no]:  yes

Enter key password for <jetty>
        (RETURN if same as keystore password):  

This creates a file named keystore in the current working directory.

  • Now add the following vm parameters to your launch config:
-Dorg.eclipse.equinox.http.jetty.https.enabled=true
-Dorg.eclipse.equinox.http.jetty.https.port=<YourHttpsPort>
-Dorg.eclipse.equinox.http.jetty.ssl.keystore=/path/to/the/created/keystore
-Dorg.eclipse.equinox.http.jetty.ssl.keypassword=password
-Dorg.eclipse.equinox.http.jetty.ssl.password=password

See also:

Why is IE6 so slow? What can be done?

Please note that IE6 is no longer supported as of RAP 1.5.

The JScript engine used in Internet Explorer 6 - Microsoft Windows Script 5.7 - is known for being slow with Ajax applications. The solutions, suggested by Microsoft are:

  • if you can upgrade to Microsoft Windows Script 5.7 - apply hotfix 919237;
  • if you have to stay with Microsoft Windows Script 5.6 - apply hotfix 942840 which includes an improved garbage collector.


For more information on this topic, please take a look at the following links:

Where to find translations/language packs?

Translations for RAP can be found at the Babel project: It provides community translated language packs for the majority of the Eclipse projects.

How do I use GZip compression with the Equinox HttpService?

Contemporary servlet engines provide for transparently compressing the response content. However, the methods used to enable this feature are container-specific. Here is an example for a Tomcat connector declaration in server.xml:

<Connector port="8080" 
           ...
           compression="on"
           compressionMinSize="1024"
           compressableMimeType="text/html,text/xml,text/plain,text/javascript,application/json"/>

When deploying RAP as standalone server (as opposed to deploying it in a WAR) the servlet engine is hidden behind the HttpService.

RAP by default ships with Equinox for the OSGi implementation and Equinox in turn uses Jetty behind the HttpService. This allows to configure the Jetty GzipFilter. The following applies to RAP running on Equinox 3.6 or later and Jetty 6.2.x.

To register the filter, use the extension below in your plugin.xml

<extension point="org.eclipse.equinox.http.registry.filters">
    <filter
        alias="/"
        class="org.mortbay.servlet.GzipFilter"
        httpcontextId="org.eclipse.rap.httpcontext"
        load-on-startup="true">
    </filter>
</extension>

For the GzipFilter class to be found, the plug-in that defines the extension must depend on org.mortbay.jetty.util

For those who prefer using OSGi services the filter can also be registered with the org.eclipse.equinox.http.servlet.ExtendedHttpService.

How to add a custom error page to a web application

The Equinox BridgeServlet has to be registered with a path mapping, i.e. a pattern that begins with a / and ends with a /*. Usually, it is mapped to /*. But this mapping prevents any other path mappings from taking effect.

To add a custom error page to an application, the BridgeServlet can be mapped to a path like /foo/*. Here's an example:

<servlet-mapping>
   <servlet-name>equinoxbridgeservlet</servlet-name>
   <url-pattern>/app/*</url-pattern>
</servlet-mapping>
 
<error-page>
   <error-code>404</error-code>
   <location>/error404.html</location>
</error-page>

Your web application will then be available at the following URL:

http://<host>:<port>/<context_path>/app/<servlet_path>

The HTTP 404 error code will be served by your custom error page error404.html, which resides in the root directory of the webapp.

Branding and Theming

How to activate the Classic Theme?

Note: The classic theme is no longer part of RAP 2.x.

To activate the classic theme add the bundle org.eclipse.rap.rwt.theme.classic to an exsiting launch configuration and change the servlet name to "classic". An alternative way to use the classic theme is to create an own branding and set the themeId to org.eclipse.rap.rwt.theme.classic.

How can I use an image in the branding body?

Using a user-defined resource inside of a RAP application requires registering it in some way. In case of images used inside the branding body, this can be done declaratively using the org.eclipse.equinox.http.registry.resources extension.

Assume you have a folder branding in the root of your plugin, containing an image called loading.gif. Add the following code to your plugin.xml:

<extension point="org.eclipse.equinox.http.registry.resources">
    <resource
        alias="/loading.gif"
        base-name="branding/loading.gif">
    </resource>
</extension>

This maps the provided resource base name to the given alias name and makes it available for use.

Access the image in your branding body by referring to the alias name:

...
    <img src="./loading.gif" border="0" />
...

Why doesn't RAP simply use native CSS for styling?

We aim to support native CSS documents at one point, but there are some obstacles to consider:

1. RAP allows for exchangeable client implementations (such as Tabris). Not all of those technologies can necessarily deal with CSS.

2. In RAP, all layouts are computed on the server side, thus the server also needs to know about any variable dimensions like paddings and borders. This is not possible if a style sheet is applied only on the client side.

3. Due to browser bugs and differences, writing cross-browser CSS code is a complex task. Currently RAP works around these issues by parsing the CSS document at applying the values itself.

How can I change the favicon / title of my RAP app?

With the help of RAPs branding features you're able to define several (visual) aspects of your RAP application. This includes for example the page title which is shown in the browser, the favicon or the theme to be used for the application. See the Branding section of the RAP Developer guide for details.

Single Sourcing

Why does Display#getDefault() work different than in SWT

One notable difference between SWT and RWT is the way the UI thread is determined. In SWT, calling new Display() marks the thread that executes the code as the UI thread. This means that the programmer is free to choose which thread should become the UI thread.

RWT, on the contrary, already provides the one and only UI thread before createUI is called (the equivalent to the main method). As a consequence, getDefault() can only create a Display instance if it is called from the user-interface thread.

How do I setup dependencies on org.eclipse.rap.ui bundles for single sourcing?

The shared package structure between org.eclipse.ui and org.eclipse.rap.ui bundles requires that dependencies be specified either via package-import or require-bundle in the bundle manifest. When single sourcing using require-bundle, it is necessary to mark the bundles as optional that are specific to either target platform, (e.g., org.eclipse.ui and org.eclipse.rap.ui). When using package-import be cautious of split-packages, see this thread for more info.

Why can't I remove warnings for missing org.eclipse.ui bundles?

Using require-bundle as mentioned above will produce warnings regarding the missing optional bundles. One method of removing these warnings is to create empty bundles that have the same bundle name and version so that they act as stubs for those that are missing.

Example org.eclipse.ui:

1. Create a manifest file for the stub bundle:

   Manifest-Version: 1.0
   Bundle-Version: 3.5.1.200812160941_stub
   Bundle-Name: RCP UI fake bundle
   Bundle-ManifestVersion: 2
   Bundle-SymbolicName: org.eclipse.ui
   Bundle-RequiredExecutionEnvironment: JavaSE-1.6


2. Place the file in a META-INF directory and zip it up.

3. Name the bundle org.eclipse.ui_3.5.1.200812160941_stub.jar

4. Place the bundle in your RAP target platform and refresh it.

Now the warnings should be gone for that bundle.

Troubleshooting

My application is not running after switching to RAP 1.5

The RAP 1.5 runtime contains an additional bundle named org.eclipse.rap.rwt.osgi. You need to include it in your launch configuration to be able to run a workbench-based application.

The Equinox console also requires three new bundles from the Apache Gogo project: org.apache.felix.gogo.command, org.apache.felix.gogo.runtime, and org.apache.felix.gogo.shell. These new bundles are contained in the basic RAP target.

IllegalStateException: No context available outside of the request service lifecycle

Your code tries to access session-information but isn't run from the UI thread (the only thread that has a session context associated by default).

The soltion is to wrap your code in a Runnable and have it executed by UICallBack#runNonUIThreadWithFakeContext().

Since RAP 2.0 use RWT.getUISession( Display ).exec( Runnable ) instead.

Please also see this thread: [1] More on session scope and session singletons can be found in the section on scopes and data stores in RWT in the RAP Developer's Guide. [edit]

NamespaceException: The alias '/rwt-resources' is already in use

This means that more than one application is started in the same context and in the same HttpService. When different applications run in the same HttpService, they have to use different context paths.

This also happens when an ApplicationConfiguration is registered AND the org.eclipse.rap.workbench bundle is running (see bug 377414).

To register an ApplicationConfiguration on a custom context using declarative services, add the following line to the service component definition:

  <property name="contextName" type="String" value="example"/>

See also ApplicationLauncher#PROPERTY_CONTEXT_NAME.

Application is not starting with "org is not defined" error in JS console

This usually points to a syntax error in JS, e.g. a missing comma between two members or a superfluous comma after the last member. Example:

   members : {
   
   	doSth : function() {
   	
   	}, // comma!
   	
   	doSthDifferent : function() {
   	
   	} // no comma!
   }

Unfortunately, Firefox and Chrome seem to more tolerant with these errors than Internet Explorer.

Locale fallback does not work as expected

You created a messages.properties file and several messages_XX.properties files for different locales. You expect the system to use the messages.properties file as a fallback when no messages_XX.file is found for the locale in question.

But that's not how RWT works. If a properties file is not found for the locale in question, RWT tries the default locale, i.e. Locale.getDefault(). This is usually the VM's locale. So if your VM is set to German, and there is a messages_de.properties file, you'll get German. If you want to have English as your fallback, set your VM to English.

Widgets disappearing in Google Chrome, Safari or Opera

This is most likely caused by a bug in the Webkit engine, as discussed in Bug 428717 - Invisible Elements in RAP Demo with latest WebKit based browsers. In summary, the issue arises under some constallations if Webkit uses hardware accelerated rendering for some of the documents element (usually Canvas), but not all. The bug has been reported to the Webkit developers here.

The issue was fixed in RAP 2.3M3. For previous versions of RAP, there are multiple alternative workarounds:

  • Disable Hardware Acceleration in your Browser

With disabled hardware acceleration this issue doesn't exist, but there can be some small impact on the performance of Canvas. In Chrome the acceleration can be disabled by visiting the URL "chrome://flags/". Look for options mentioning "GPU" and enable/disable them as needed.

  • Force Hardware Acceleration for the entire Page

In the entry point of your application, add the following lines:

   JavaScriptExecutor jse = RWT.getClient().getService( JavaScriptExecutor.class );
   jse.execute( "var style = document.createElement( \"style\" );\n" +
                "style.type = 'text/css';\n" +
                "style.innerHTML = \"div { -webkit-transform: translate3d(0, 0, 0); }\";\n" +
                "document.getElementsByTagName( \"head\" )[ 0 ].appendChild( style );" );


Note that there have been reports of this workaround to cause other visual glitches (specifically in Chrome 35+), and the impact on performance is unkown. Overall it is not recommended. At the very least you should limit the hack it to webkit versions that have been tested (using the navigator string).

  • Use Background Gradients in your theme instead of Background Colors

Somehow the issue is connected to the usage of the CSS attribute background-color on Composites, but does not appear when using gradients. Therefore, one relatively simple solution is to always set a gradient when setting background color, using the same values. To do so, you need to register a theme contribution with the following content:

 Composite, Group {
   background-image: gradient(
     linear, left top, left bottom,
     from( #ffffff ),
     to( #ffffff )
   );
 }

However, if you use a theme contribution/custom theme with custom variants to set the background colors of Composites you need to updated those also. Wherever you set a background color other than white you also need to set a background gradient with the same colors. (Otherwise the above rule overwrites that color.)

Example:

 .header {
   background-color: #31619C;
   background-image: gradient(
     linear, left top, left bottom,
     from( #31619C ),
     to( #31619C )
   );
 }

In case of transparent background, it has to look like this:

 .header {
   background-color: transparent;
   background-image: none;
 }

Blank page or client crash in Firefox 29+

This is likely caused by "Bug 433179 - RAP application does not start in Firefox 29 (beta) ". It was fixed in RAP 2.3M3. For previous RAP versions, follow these steps:

If your application is started by ApplicationConfiguration, add the follwing line there:

     properties.put( WebClient.HEAD_HTML, "<script type=\"text/javascript\">(function(){try{var e=navigator.userAgent;if(e.indexOf(\"like Gecko\")===-1&&e.indexOf(\"Gecko/\")!==-1){if(!window.controllers){window.controllers={}}if(!navigator.product){navigator.product=\"Gecko\"}}}catch(t){}})()</script>" );

If your application is started by an org.eclipse.rap.ui.entrypoint extension, create a branding to add a body HTML file (in case you don't have one already). In this HTML file add the following:

 <script type="text/javascript">(function(){try{var e=navigator.userAgent;if(e.indexOf("like Gecko")===-1&&e.indexOf("Gecko/")!==-1){if(!window.controllers){window.controllers={}}if(!navigator.product){navigator.product="Gecko"}}}catch(t){}})()</script>


Scrolling looks broken in Google Chrome, Safari or Opera

This is similar to RAP/FAQ#Widgets disappearing in Google Chrome, Safari or Opera. The workaround "Force Hardware Acceleration for the entire Page" mentioned there also fixes this issue. At this time the exact cause is not known, but it is very likely related to hardware acceleration. We hope to find a propper fix for this issue to apply it to RAP 3.0 This is also discussed in this bug report.

Back to the top