Wednesday, November 28, 2007

RAP Deployment - Part 1: Deploying your application with Jetty

When it comes to deploying your RAP Application you have two options: you can either put the web server inside the OSGi runtime (Equinox + Jetty + RAP + your app) or you can put the OSGi runtime inside an existing server (.war file with the Servlet Bridge + Equinox + RAP + your app).

I recently spent some time exploring both options and found the process of "getting it right" intimidating (at least the first time). To give you a head start I'll share my experiences. If you find a simpler way of doing this let me know ;-).


Part 1: Deploy with Jetty inside Equinox

This is the same technique used by the RAP launcher to start your application when you launch it from inside Eclipse (a.k.a "self-hosting" deployment). We will create a set of plug-ins containing Equinox (OSGi)pl, Jetty, RAP, your application and the Equinox launcher.

Watch the screencast (approx. 7 minutes) or read-on for more details.

Below are the steps shown in the screencast. You can also download the calculator's source code here.
  1. Create a feature for your application

  2. Add the all dependencies of your application to it (e.g. RAP and all the plug-ins you use)

  3. Add the Jetty server to your feature as explained in "Embedding an HTTP server in Equinox". You 'll need these plug-ins:

    • org.eclipse.equinox.http.jetty
    • org.eclipse.equinox.http.servlet
    • org.mortbay.jetty
    • org.apache.commons.logging
    • javax.servlet
    • org.eclipse.equinox.http.registry

  4. Export the feature using "File > Export > Deployable features"

  5. Copy the Update Configurator (provides auto-discovery of plug-ins) and the Equinox Launcher to the deployment directory, as explained in the "Equinox Quickstart Guide". You'll need to copy those files to the locations listed below. You can get the files from your Eclipse installation.

    • your_deployment_dir\eclipse.exe
    • your_deployment_dir\plugins\org.eclipse.equinox.launcher_...jar
    • your_deployment_dir\plugins\org.eclipse.equinox.launcher.win32.win32.x86_... (with sub-directories, this will be named different on other platforms)
    • your_deployment_dir\plugins\org.eclipse.update.configurator_...jar

  6. Create the file your_deployment_dir\configuration\config.ini as shown:

    #bundles to start osgi.bundles=org.eclipse.equinox.common@2:start,org.eclipse.update.configurator@3:start,org.eclipse.rap.ui@4:start,org.eclipse.equinox.http.jetty@4:start

    # options for the equinox launcher
    # do not run an eclipse application
    eclipse.ignoreApp=true
    # don't shutdown osgi after exiting (or not running) the application

    osgi.noShutdown=true

    # incoming port for the server
    org.osgi.service.http.port=7070

  7. Go into your_deployment_dir and launch:

    eclipse -console

  8. Type "ss" on the OSGi console. If you have unresolved (=INSTALLED) bundles, you missed some dependencies. Also check the .log file under your_deployment_dir\workspace\.metadata\.log. If you see something other than "Authorization infrastructure (org.eclipse.core.runtime.compatibility.auth) not installed" you probably missed some dependencies. In my case I ignored the aforementioned message because I don't need that optional plug-in in my deployment.

  9. Your app should now be reachable under:

    http://localhost:7070/rap
Come back next week to find out how to deploy this application as a .war file.

32 comments:

Bull said...

Great Work! I have been also deploying a web server using this technique and I am happy to see that I did it correctly :-). Thanks for properly documenting this. I am very interested in seeing the alternative deployment method, since I could not figure that one out.

bstarchev said...

Hi,
I have imported and started project calc.ui.rap after Run as; OSGi Framework
In console I have:
osgi> 2007-12-3 20:08:34 org.mortbay.http.HttpServer doStart
INFO: Version Jetty/5.1.x
2007-12-3 20:08:34 org.mortbay.util.Container start
INFO: Started org.mortbay.jetty.servlet.ServletHandler@16dc861
2007-12-3 20:08:34 org.mortbay.util.Container start
INFO: Started HttpContext[/,/]
2007-12-3 20:08:35 org.mortbay.http.SocketListener start
INFO: Started SocketListener on 0.0.0.0:80
2007-12-3 20:08:35 org.mortbay.util.Container start
INFO: Started org.mortbay.http.HttpServer@7cbde6

But when connected by:
IE/Firefox: http://127.0.0.1:80/rap

I have received not calculator but a window “Workbench Demo”


How to solve the problem

Boris Starchev teacher

Anonymous said...

very, very good work! thank you, I´m looking forward for more stuff like this, since at the moment there is still a lack on good documentation. Thank you very much

Elias Volanakis said...

Hi Boris,

if the Workbench Demo shows up, you should probably check the list of Bundles in the Launch Configuration. Make sure calc.ui.rap is checked and org.eclipse.rap.demo is *not* checked.

You can see the list by opening Run > Open Run Dialog... > [select your configuration] > Bundles Tab.

Here are the bundles I've checked in my launch configuration:

calc.ui.rap
javax.servlet
org.apache.commons.logging
org.eclipse.core.commands
org.eclipse.core.contenttype
org.eclipse.core.expressions
org.eclipse.core.jobs
org.eclipse.core.runtime
org.eclipse.equinox.app
org.eclipse.equinox.common
org.eclipse.equinox.http.jetty
org.eclipse.equinox.http.registry
org.eclipse.equinox.http.servlet
org.eclipse.equinox.preferences
org.eclipse.equinox.registry
org.eclipse.osgi
org.eclipse.osg.services
org.eclipse.rap.jface
org.eclipse.rap.rwt
org.eclipse.rap.ui
org.eclipse.rap.ui.workbench
org.mortbay.jetty

(22 total)

Let me know if this helped.

Regards,
Elias.

Anonymous said...

Hi, Elias.
Thanks for the tip. Do you know when you are going to be able to post a blog on part 2? I have tried to deployed my .war file on a Tomcat server as indicated on http://help.eclipse.org/help33/index.jsp?topic=/org.eclipse.rap.help/help/html/advanced/deployment.html without success.
Thanks, Anon

Elias Volanakis said...

I'll post 2 by the end of this week.

Regards,
Elias.

Anonymous said...

What about shapes and figures in RAP?
Thanks...

Elias Volanakis said...

Hi anonymous,

there is no support for 2D geometric figures out of the box. However you could always implement your own custom widgets.

Elias.

Elias Volanakis said...

For those who have asked about Part 2 - I just posted it: Deploying RAP in a .war file

bstarchev said...

Hi, Elias

Thanks for the help.
You are right. It is necessary to uncheck org.eclipse.rap.demo and probably in Launch Configuration > Settings: Check: Clear the configuration area before launching.


If two bundles calc.ui.rap and org.eclipse.rap.demo are checked there are some problems because of two entry points with “same name” – default.

!entrypoint class="calc.ui.rap.CalculatorApplication" parameter="default" id="calc.ui.rap.CalculatorApplication" /!

!entrypoint class="org.eclipse.rap.demo.DemoWorkbench" id="org.eclipse.rap.demo.entrypoint1" parameter="default" /!

There are some “unpredictable” behavior in Launch Configuration.
This can be seen in next tree step experiment:


1)Close project calc.ui.rap > new Launch Configuration > Run > Internal Web Browser: http://localhost/rap Workbench Demo

2)Open project calc.ui.rap > Right Click calc.ui.rap > Run as > OSGi Framework > Internal Web Browser: http://localhost/rap Workbench Demo

3)Open your Launch Configuration > Settings > Check: Clear the configuration area before launching > Run > Internal Web Browser: http://localhost/rap Calculator

Some times it is necessary to restart Eclipse (3.3.1.1) to avoid message :
WARNING: Failed to start: SocketListener0@0.0.0.0:80

Boris Starchev teacher

arcy said...

Thanks for this article!

Tested on:

Eclipse 3.4.M3, with target, eclipse-rap-1.0.1-R-target-3.3-20071207-1851

Now, if I could just get GMap example to work ;-)

R

David Donohue said...

Elias,
Thanks for this great screencast & article! You solved the problem that I was having when I tried to start RAP like this

java -Dlog4j.debug -jar org.eclipse.osgi_3.3.0.v20070530.jar -configuration configuration -consoleLog -debug -clean -dev -console

Here was the error that I (and others) was getting:
java.lang.NoClassDefFoundError: org/xml/sax/SAXException

So be advised, peoples, that this is the solution!
David Donohue

David Donohue said...

Elias,
I found a way to do the whole deploy without needing to manually add upon each build the eclipse.exe file and the configuration folder (containing config.ini). So now I just export my feature as "Deployable Features" and run eclipse -console.

Here's what I did:
(1) to build.properties, add these lines
customBuildCallbacks=customBuildCallbacks.xml
customBuildCallbacks.inheritall=true

(2) In my feature, create a folder dist_root. Add to this dist_root, the eclipse.exe and configuration folders, and any other files and folders you want. The contents of dist_root will be exported to the root folder (alongside "features" and "plugins" folders).

(3) In the root of the feature, add the custom callback ant build file customBuildCallbacks.xml with this content:

<project name="Build specific targets and properties" default="noDefault">

<target name="noDefault">
<echo message="This file must be called with explicit targets" />
</target>


<!-- Before export, this deletes the directory to which we are deploying -->
<target name="pre.gather.bin.parts">
<delete includeemptydirs="true">
<fileset dir="${feature.directory}/../../" includes="**/*"/>
</delete>
</target>


<!-- After export, this copies the contents of the folder dist_root to the directory to which we are deploying -->
<target name="post.gather.bin.parts">
<copy todir="${feature.directory}/../../" includeemptydirs="false">
<fileset dir="dist_root">
</fileset>
</copy>
</target>

</project>


I also added all the bundles (launch bundles, update configurator) to my feature's plug-ins, so those are added upon export too.

David Donohue

Elias Volanakis said...

Hi David,

that's a clever trick. Thanks for sharing it with us!

Elias.

Anonymous said...

Hello,

Thanks for the good work.

But I have one problem, when setting my own body HTML page in the org.eclipse.rap.ui.branding extension of the application, it works fine, when running the app from within Eclipse, but deploying it (as described in this blog entry) as a "stand-alone" application, it fails.
The servlet is not found:
HTTP 404 error, and the bundle is not started. The status stays on "LAZY".

Starting the bundle with "start", I get the following exception:

osgi> start 8
org.osgi.framework.BundleException: The activator rap.Activator for bundle RAP is invalid
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadBundleActivator(AbstractBundle.java:141)

...

So, what must I do to get this working? Or is it not possible in this kind of deployment?

I have not found any solution by my own, so far.

Christian

Elias Volanakis said...

Hi Christian,

with the information you provide it is very hard for me to "guess" what might be going wrong.

I guess that it could be a bug or a problem with your configuration.

I suggest opening a bug against Technoly/RAP that contains an example that can be used to reproduce this issue. This way I or my colleagues from the RAP team can have a look at it.

You can open a bug here. Please put me on the CC field.

Regards,
Elias.

Anonymous said...

Ohhhhhhh myyyyyyyy God...

The error was the the typical user error. During my testing and probing, I have created more than one body page, so it all ended up in setting html/page1.html as the body parameter in the branding extension, but selecting html/page2.html in the "Build Configuration" tab of the plugin.

Sorry for making that unnecessary noise :-( and shame on me...

Greetings,
Christian

P.S. Why is it always, that after asking for help, one finds the solution for oneself.

Elias Volanakis said...

Hi Christian,

I'm glad this could be solved. Happy hacking with RAP.

Regards,
Elias.

Yuksel said...

Nice presentation.

I have two questions:

1) Is it possible to deploy a RAP application in a Linux environment?

2) An architectural picture of RAP that I saw on RAP wiki gave me the impression that it does not require platform specific pieces to run. Is that correct to assume?

Elias Volanakis said...

Hi yuksel,

yes, you're right. RAP does not require any platform specific bundles to run. You should be able to deploy it anywhere where you have a Java VM (1.4.x or newer).

Regards,
Elias.

David Donohue said...

Thanks!
How can we launch our deployed RAP application in Linux or Mac?

David Donohue said...

I think I figured out how to get my standalone RAP application to deploy and run on Linux or Mac. I deployed as described in this most helpful post, but I had to make a separate config folder for when executing from Mac or Linux Shell. Also there is a trick to workaround the dreaded NoClassDefFoundError: org.xml.sax.SAXException.

I describe in more detail here
http://www.eclipse.org/newsportal/article.php?id=3252&group=eclipse.technology.rap#3252

Elias Volanakis said...

David,

great to hear you were able to deploy on Mac and Linux. Thanks for sharing this information with us.

Regards,
Elias.

Anonymous said...

Hi,
I encountered a problem I can't solve.
I follow strictly the instructions but I get allways following message:

java.lang.RuntimeException: No application id has been found.
at org.eclipse.equinox.internal.app.EclipseAppContainer.startDefaultApp(EclipseAppContainer.java:236)
at org.eclipse.equinox.internal.app.MainApplicationLauncher.run(MainApplicationLauncher.java:29)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:379)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:508)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:447)
at org.eclipse.equinox.launcher.Main.run(Main.java:1173)

Anonymous said...

Hi, I tried to deploy this rap on macosx (10.4/MacIntel). I strictly followed the same steps described previously, but nothing is displayed: I got a HTTP ERROR 404 at http://127.0.0.1:7070/rap.

Here is the log:

~/temp/calc.jetty danh$ Eclipse.app/Contents/MacOS/eclipse -console

osgi> 18 juin 2008 00:45:25 org.mortbay.http.HttpServer doStart
INFO: Version Jetty/5.1.x
18 juin 2008 00:45:25 org.mortbay.util.Container start
INFO: Started org.eclipse.equinox.http.jetty.internal.Servlet25Handler@8a1c9d
18 juin 2008 00:45:25 org.mortbay.util.Container start
INFO: Started HttpContext[/,/]
18 juin 2008 00:45:25 org.mortbay.http.SocketListener start
INFO: Started SocketListener on 0.0.0.0:7070
18 juin 2008 00:45:25 org.mortbay.util.Container start
INFO: Started org.mortbay.http.HttpServer@870147


osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
1 ACTIVE org.eclipse.equinox.common_3.4.0.v20080421-2006
2 ACTIVE org.eclipse.update.configurator_3.2.101.R33x_v20070810
3 ACTIVE org.eclipse.rap.ui_1.1.0.20080613-1055
4 ACTIVE org.eclipse.equinox.http.jetty_1.1.0.v20080425
5 LAZY calc.ui.rap_1.0.0
6 LAZY com.ibm.icu_3.8.1.v20080530
7 RESOLVED javax.servlet_2.4.0.v200806031604
8 RESOLVED org.apache.commons.logging_1.0.4.v20080605-1930
9 RESOLVED org.eclipse.core.commands_3.4.0.I20080509-2000
10 LAZY org.eclipse.core.contenttype_3.3.0.v20080604-1400
11 LAZY org.eclipse.core.databinding_1.1.0.I20080527-2000
12 LAZY org.eclipse.core.expressions_3.4.0.v20080603-2000
13 ACTIVE org.eclipse.core.jobs_3.4.0.v20080512
14 ACTIVE org.eclipse.core.runtime_3.4.0.v20080512
15 ACTIVE org.eclipse.equinox.app_1.1.0.v20080421-2006
18 RESOLVED org.eclipse.equinox.http.registry_1.0.100.v20080427-0830
19 ACTIVE org.eclipse.equinox.http.servlet_1.0.100.v20080427-0830
20 RESOLVED org.eclipse.equinox.launcher.carbon.macosx_1.0.3.R33x_v20080118
Master=21
21 RESOLVED org.eclipse.equinox.launcher_1.0.1.R33x_v20080118
Fragments=20
22 ACTIVE org.eclipse.equinox.preferences_3.2.200.v20080421-2006
23 ACTIVE org.eclipse.equinox.registry_3.4.0.v20080516-0950
24 RESOLVED org.eclipse.osgi.services_3.1.200.v20071203
25 RESOLVED org.eclipse.rap.jface.databinding_1.1.0.20080613-1055
26 ACTIVE org.eclipse.rap.jface_1.1.0.20080613-1055
27 RESOLVED org.eclipse.rap.rwt.q07_1.1.0.20080613-1055
Master=28
28 RESOLVED org.eclipse.rap.rwt_1.1.0.20080613-1055
Fragments=27
29 ACTIVE org.eclipse.rap.ui.workbench_1.1.0.20080613-1055
32 RESOLVED org.mortbay.jetty_5.1.14.v200806031611

I hope this will help to debug my problem. Thanks in advance.

Danh

Anonymous said...

Some other feedback,
when I ran calc.ui.rap project from wihtin Eclipse (I made sure to only select calc.ui.rap bundle in the run dialog).

This time i got a HTTP ERROR 500.

Here is the log from Eclipse:

osgi> 18 juin 2008 00:57:17 org.mortbay.http.HttpServer doStart
INFO: Version Jetty/5.1.x
18 juin 2008 00:57:17 org.mortbay.util.Container start
INFO: Started org.eclipse.equinox.http.jetty.internal.Servlet25Handler@4a2ec6
18 juin 2008 00:57:17 org.mortbay.util.Container start
INFO: Started HttpContext[/,/]
18 juin 2008 00:57:17 org.mortbay.http.SocketListener start
INFO: Started SocketListener on 0.0.0.0:63549
18 juin 2008 00:57:17 org.mortbay.util.Container start
INFO: Started org.mortbay.http.HttpServer@89eb77
18 juin 2008 00:57:24 org.mortbay.jetty.servlet.ServletHandler handle
GRAVE: /rap?nocache=1213743444642:
org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:1683)
at org.eclipse.swt.SWT.error(SWT.java:1603)
at org.eclipse.swt.SWT.error(SWT.java:1574)
at org.eclipse.swt.widgets.Widget.error(Widget.java:775)
at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:718)
at org.eclipse.swt.widgets.Composite.getChildren(Composite.java:107)
at org.eclipse.swt.internal.widgets.WidgetTreeVisitor.accept(WidgetTreeVisitor.java:49)
at org.eclipse.rwt.lifecycle.WidgetUtil.find(WidgetUtil.java:198)
at org.eclipse.swt.internal.widgets.displaykit.DisplayLCA.readFocusControl(DisplayLCA.java:484)
at org.eclipse.swt.internal.widgets.displaykit.DisplayLCA.readData(DisplayLCA.java:255)
at org.eclipse.rwt.internal.lifecycle.ReadData.execute(ReadData.java:26)
at org.eclipse.rwt.internal.lifecycle.RWTLifeCycle.continueLifeCycle(RWTLifeCycle.java:208)
at org.eclipse.rwt.internal.lifecycle.RWTLifeCycle$UIThreadController.run(RWTLifeCycle.java:115)
at java.lang.Thread.run(Thread.java:613)

I do not know what i am doing wrong. The calc appears very furtively then i got the error page. I am stuck.

Danh

Elias Volanakis said...

Hi dahn,

in general you should get the calc app to run withing Eclipse before trying deploying it.

The Invalid Thread Access exception in the stack trace seems strange. Which version of RAP are you using? I believe I used RAP 1.1 M3.

Regarding the no application id error. If that happens in a deployed app it is very likely a problem with the build.properties. Double click build.properties and check that files like plugin.xml, META-INF, icons etc. are checked where it says "Binary build. Select files and folders to include". Otherwise those files do not get copied into the plugin.jar when you export.

Kind regards,
Elias.

Anonymous said...

Hi Elias,

thanks for your advices.

I use the latest RAP build: rap-sdk-1.1.0-RC4-site-20080613-1119.zip
i did install it via the update site.
For info, my Eclipse version Version: 3.3.2
Build id: M20080221-1800

I checked for the build properties, and in the binary build section I ticked some supplementary files with the default files folders selected. Then I exportde novo the deployable features. I run again the rap application but nothing changed in the deployed calc (http error 404). I compared the osgi console log in your video with mine, and i saw that your bundle calc.rap.ui is active but mine is lazy. I do not know it is the case, but maybe there is a relationship. what do you think?

I did run the calc within eclipse with no success before. It is frustrating because i did see very quickly in fact the calc composite then it disappears, the error page coming to front in the eclipse browser (http error 500). I am still wondering why i am stuck.

Danh

Anonymous said...

Hi

i would like to mention some change
in the method signature createUI, the return type is an int since the introduction of Display#readAndDispatch() in 1.1M2.

Eclipse notifies me an error in the CalculatorApplication class. So I replaced the old return type Display with the constant int PlatformUI.RETURN_OK. But nothing changed when running calc from within eclipse. The Calc composite appears then the error page comes to front (500). The console log always throws invalid thread access in SWT. A bug with the M4 release?

Danh

Anonymous said...

Hi

it's me again, I made some changes recommended by the notes in the rap M2 build concerning the createUI method. So in the CalculatorApplication class, I changed the return type to int and added a loop into this entrypoint:

public class CalculatorApplication implements IEntryPoint {

public int createUI() {
final Display display = PlatformUI.createDisplay();

...
// my own change
while( !shell.isDisposed() ) {
if( !display.readAndDispatch() ) {
display.sleep();
}
}
return 0;
}
}

Then i ran again from within eclipse and now it works perfectly.
No more invalid thread access error.

But in the deployed case, i still have http error 404. I checked for dependencies, the calc ones, the jetty ones and the eclipse launcher and update configurator ones. But still no success, the calc.rap.ui bundle is still lazy. Very frustrating...

Danh

Anonymous said...

I gave the last debug info for deploying calc on macosx.

I've had the http error 404 from jetty in firefox because some bundles were not active at the launch/run time (but if tested within eclipse all bundles are active, and the test was positive).

In the terminal, at the osgi console prompt, i typed ss to list all bundles currently used by calc.
And for every bundle with status "resolved" or "lazy", I activated them by typing:

start "bundle id"

When I was done, I exited from osgi console and restart calc. Typing again ss in the console, i noticed that this time all bundle were activated. The framework seems to remember the settings.

And finally, in firefox, at the url http://localhost:7070/rap, i can see the calc rap application.

Thanks again for this great app!

Danh

Elias Volanakis said...

Hi Danh,

great to hear you were able to get it working :-).

Kind regards,
Elias.