What is the first thing you see when you start an RCP product? The splash screen.
What is the second thing you see when you start an Eclipse RCP product? The intro page. This page is the first interaction that the user has with your product. It deserves real effort to ensure that your users feel comfortable when opening the product. Also, you can leverage this page to add dynamic content, that can be used, for example, for direct communication between your company and your users. Then it can become a tool for both user-friendliness and marketing, with a relatively low effort. It’s worth it!
In this post we will describe how we leverage the intro page extensibility and API to develop a more ergonomic and marketing-friendly intro page. Everything you will read here can be found or taken directly from the Eclipse Help contents, but I’ll try to explain it more practically. Read the rest of this entry »
In a previous post, I explained how it is possible to add complex widgets made with JavaScript in a Bonita Form Application, and also how this widget can interact with the data in your process. That example widget was a Google Map.
In this post, I’ll start with one of the user requests in the Bonita Community bug tracker, in order to show you how to embed and leverage RIA JavaScript framework, such as jQuery, in your application. We’ll create a dialog box in a form, and as you’ll see, it’s easy.
Add jQuery resources to your application
This is an example of why there is a Resource tab. First get all the necessary resources from jQuery by downloading them from the website, and add all the folders you need in the applications folder.
That’s all!
Tweak your main HTML page to include jQuery
This is a mandatory step that you have to perform whenever you want to add JavaScript to your applocation. First, retrieve the BonitaApplication.html that you can get by hitting Run, and show the source code for the page. This is your main HTML page. Then add the necessary <script…> headers to the head element of the page, in order for jQuery to run.
And then, simply add the modified BonitaApplication.html in the application resource folder.
Now, use jQuery!
jQuery is added and enabled in your application. Just do it! Put an HTML widget in your form, and add the content below (copy-pasted from jQuery documentation):
<divid="dialog"title="Basic dialog">This is a dialog "popped-up" from an application generated with Bonita Open Solution!</div>
In some cases, you can put the script headers wherever you want in the HTML page, and you can put it in other templates for your application, or directly in the HTML widget. However, this is not a good practice for compatibility, and the only way to ensure your scripts are loaded is to put them in BonitaApplication.html.
If your jQuery widget has any input fields, use callbacks on them to set the value of the fields defined in your form designed in Bonita Studio. Add “hidden field” for the data handled by the widget, and change the value of the hidden input with a callback. See the Google Maps example in previous post for a more complete example.
And while I am writing about dialog boxes, I’m hearing this music. Coincidence? I don’t think so. AEED – Dialog Box by AEED
Here is an explanation of how to add your own custom widgets to the applications generated by Bonita Open Solution. This is something a lot of Bonita Open Solution users have been asking for on our forum, and here is one solution!
In this mini-tutorial, I’ll use Bonita Open Solution 5.4 with its new HTML widget in the form designer, but the same method can be applied to older releases of BOS. You’ll simply have to use a Message widget with the “Allow HTML” flag set to true.
Example 1: Using the Google Maps API
Google provides a great JavaScript API for almost all its products. Let’s use the Google Maps API for the interaction between the Forms application of your process and a Google Map. The user will see – instead of 2 text widgets to input longitude and latitude – a Google Map with a marker to drag and drop in order to indicate a location. Let’s start with a simple process with 2 text variables: latitude and longitude.
The process with its data
Your widgets will make your pages nicer!
If you really don’t want to do it yourself, you can find this example process in the community contributions.
Step 1: Reference the API in BonitaEnvironment.html
In order to leverage the API, you need to define a reference to it in your application. In my opinion, that’s the trickiest part, although it’s still quite easy, and we’ll probably make this step even easier to do in a future release.
You need to add the following lines to the header of the BonitaApplication.html file:
Then create a new process and run it, get the source of form page, this is BonitaApplication.html. Add the necessary lines. The result should look like this:
<!-- OPTIONAL: include this if you want history support -->
<!-- loading displayed while GWT is not yet ready --> <divid="loading"> <divid="bottom-right-shadow"> <divid="loading-content"><imgsrc="images/large-loading.gif"alt=""/><!-- Please wait while loading... --></div> </div> </div>
Then, add it to your Forms resources so that the default BonitaApplication.html is replaced by the one you just tweaked:
Add BonitaApplication.html to your application resources
Step 2: Model your form
Your form will be very simple to model. It is simply made of 2 hidden fields: latitude and longitude. These widgets add 2 input fields you’ll use to store and save the location marked in the Google Map. Since a Google Map is not a basic HTML input, we cannot directly retrieve its values to store them later. Then we’ll make the Google Map widget store the data we want to use later in those fields.
Then add your HTML widget (or Message widget allowing HTML in older versions). Put in the following code as the initial value:
<scripttype="text/javascript">// <![CDATA[ var map;
var myLatlng = new google.maps.LatLng(45.18409395583682,5.703503018447886); var myOptions ={ zoom: 4, center: myLatlng, mapTypeId: google.maps.MapTypeId.HYBRID } map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var marker = new google.maps.Marker({ position: myLatlng, map: map, title:"Where are you?", draggable: true }); google.maps.event.addListener(marker, 'dragend', function(){ var point = marker.getPosition(); // Update fields. document.getElementById("latitude").getElementsByTagName("input")[0].value= point.lat(); document.getElementById("longitude").getElementsByTagName("input")[0].value= point.lng(); }); //]]></script>
This script adds a map to your page and updates the hidden fields whenever you move the marker. As you can see, this is simply HTML, JavaScript and Google Maps API, nothing more!
Form overview
Step 3: Try and enjoy!
Just hit the “Run” button, and take a look at your application. It works! Wasn’t that easy?
We can’t say this enough: tests are important for code quality. When writing a connector for
Bonita Open Solution, it’s useful to create such tests. And it’s easy to do that.
For each connector, create a classical jUnit TestCase. Then, two kind of tests may be done:
test if the connector description is well written; and
test the connector itself.
The following explains how to do this.
How to test the description of the connector “MyConnector”
The procedure is quite simple. We get the connector class, call the method
Connector.validateConnector() on it, and then get back the errors. If the connector is
valid, no error is encountered.
publicvoid testValidateConnector()throws BonitaException { Class connectorClass = MyConnector.class; List errors =
Connector.validateConnector(connectorClass);
assertTrue(errors.isEmpty()); }
A year and a half after the first draft of Bonita Studio, we decided to move our product build from plugins to features, in order to better handle internationalization, and with the hope to better factorize the build of our 2 products: BOS and BOS-SP (which is a set of extensions on top of BOS).
Our build still uses the highly customized headless PDE-build wrapped in a master script responsible for packaging and testing the application as we deliver it. Here are the steps we followed:
Create the feature from the product definition
This was not a trivial task, since there is no tool to transform a product definition to a feature. The solution we chose was to use Run Configuration to create the feature. This is an intermediate step that is useful to turn a product definition into a feature:
Right-click on your *.product, and Run as Eclipse application
The Run Configuration matching the definition of your .product (and then the set of plugins that it contains) is created
Create a new feature, and use the magic Create from Run Configuration button
Remove the launchers (org.eclipse.equinox.launcher*) from this feature
This method is inspired from blog posts written by Manuel Selva.
Redefine your *.product
From there, you can redefine your .product to include only 2 features: the one you just created, and the org.eclipse.rcp feature.
Quickly validate it
You can make a first validation of your product. The steps are easy:
Create a launch configuration for your product by clicking on the Run As Eclipse application or the Synchronize entry
Launch product from product editor
Open the launch configuration wizard, and click on the validate button
The validate button will give you some hints to fix deps
It tells you about some static missing dependencies. Correct your feature according to its advices.
Be patient and keep retrying when you get errors. I think I needed more than a dozen iterations before getting the happy button telling me all is well.
Add the feature to your map
This step is only mandatory if you have a headless build that uses map files. If you usually build using UI, just skip it.
Happy HEADLESS Halloween !
Everything is in the title: you just created a new feature, and you’ll need it at build time. Simply add an entry for the feature to your map:
!** Features
feature@org.bonitasoft.studio=SVN,url=http://svn.bonitasoft.org/,tag=bonita-studio,path=XXX_TAG_XXX/releng/org.bonitasoft.studio-feature
Try, see and troubleshoot
Once you’ve done all that, try to build your product. Using the UI entry should be enough.
The export product wizard
Fixing startup
After that, you can give a first try to your bundled product. Run it. It may fail to start. In this case, analyze the startup log that you will find in YourProduct/config/[timestamp].log. You’ll probably see some lines such as these:
This simply tells you the missing bundles in your feature. Then, for each Missing required bundle entry, you will need to add it to your feature, take a look at its dependency tree, add some its dependencies if you missed them, and… Retry !
Fixing runtime
It can easily happen that one of your plugins does not start without crashing the whole product. In such case, you silently lose the features provided by the code of your plugin. Then you can use the OSGi console to get insight about what’s going wrong.
In this example, the log tells me that it cannot load the test plugin, but I don’t really know why. So I can try the following:
mistria@mistri-laptop:~/BonitaStudio-20101021$ ./BonitaStudio -console
[... Some more or less useful stuff ...]
osgi> start org.bonitasoft.studio.tests
org.osgi.framework.BundleException: The bundle "org.bonitasoft.studio.tests_1.0.0.20101021-2140 [1449]" could not be resolved. Reason: Missing Constraint: Require-Bundle: org.bonitasoft.studio.repository.test; bundle-version="0.0.0"
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolverError(AbstractBundle.java:1317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolutionFailureException(AbstractBundle.java:1301)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:319)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:284)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:276)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:252)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:155)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:156)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.runConsole(FrameworkConsole.java:141)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:105)
at java.lang.Thread.run(Thread.java:595)
osgi> start org.bonitasoft.studio.repository.test
org.osgi.framework.BundleException: The bundle "org.bonitasoft.studio.repository.test_1.0.0.20101021-2140 [1340]" could not be resolved. Reason: Missing Constraint: Require-Bundle: org.bonitasoft.studio.util.tests; bundle-version="1.0.0"
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolverError(AbstractBundle.java:1317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolutionFailureException(AbstractBundle.java:1301)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:319)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:284)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:276)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:252)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:155)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:156)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.runConsole(FrameworkConsole.java:141)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:105)
at java.lang.Thread.run(Thread.java:595)
osgi> start org.bonitasoft.studio.util.tests
org.osgi.framework.BundleException: The bundle "org.bonitasoft.studio.util.tests_1.0.0.20101021-2140 [1371]" could not be resolved. Reason: Missing Constraint: Require-Bundle: org.eclipse.swtbot.eclipse.finder; bundle-version="2.0.0"
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolverError(AbstractBundle.java:1317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolutionFailureException(AbstractBundle.java:1301)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:319)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:284)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:276)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:252)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:155)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:156)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.runConsole(FrameworkConsole.java:141)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:105)
at java.lang.Thread.run(Thread.java:595)
org.osgi.framework.BundleException: The bundle "org.eclipse.swtbot.eclipse.finder_2.0.0.568-dev-e36 [1301]" could not be resolved. Reason: Missing Constraint: Require-Bundle: org.eclipse.swtbot.swt.finder; bundle-version="2.0.0"
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolverError(AbstractBundle.java:1317)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.getResolutionFailureException(AbstractBundle.java:1301)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:319)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:284)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:276)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:252)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:155)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:156)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.runConsole(FrameworkConsole.java:141)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:105)
at java.lang.Thread.run(Thread.java:595)
Thanks to the console, I can figure out that the missing bundle is org.hamcrest library. I just add it to my feature, and everything works better. Using the console is a general method to resolve dependencies issues. The console is often your best friend!
Conclusion
As you can see, switching from a plugins-based product to a feature-based is not immediate. Features do not provide the fantasically useful Add Required Plug-ins button that you can find on a plugin-based product, and so this move will probably lead to errors in dependency management.
The "Add Required Plug-ins" button is only available for plugin-based products
That’s why, once you have your feature, you need to tweak it to get your product working, since you can easily miss a bundle. Moreover, the bundles you ship may have a different version after a feature-based build. You’ll need to test it well to ensure that this move did not break some features of your product because of bundle versioning. I opened bugs 328323 and 319085 to ask for improvments on this topic.
As always, automated non-regression tests are your very best friend for such a move.
Our feature-based build has now been working for a few days, and I can already tell I am quite happy with it. The build of our 2 products is easier to understand and maintain, and now looks like a simple composition of features. Also, adding a language does not require us to modify the .product any more, and it helps us to get closer to the ability to provide “language packs” as extensions of the product.
Next step, moving to p2…but that will be another story!
By the way…
Aurélien and I will be at Eclipse Summit in Ludwigsburg. Aurélien will have the opportunity to present to the Eclipse community how we leverage the Modeling projects in our product.
If you want to have a chat with us, about anything, feel free to drop us an email to plan a meeting, or grab us during the event!