diff --git a/_book/150-tutorials.md b/_book/150-tutorials.md index 20154c4..f4feefc 100644 --- a/_book/150-tutorials.md +++ b/_book/150-tutorials.md @@ -5,6 +5,19 @@ layout: default This section is an entry to the hopefully growing collection of tutorials that OSGi enRoute provides. If you want to develop an additional tutorial, please submit a PR. +## Quick Start + +![Thumbnail for Quick start Tutorial](/img/qs/app-0.png) +{: .thumb200-l } + +In this quick start we develop a little project that, creates a single page web-application. This tutorial is light on the explanations because it focuses on introducing the overall architecture of enRoute, not the details. We will cover the whole chain, from creating a workspace all the way to continuous integration. + +A disclaimer. This quick start is about learning to use OSGi enRoute, not about learning Java, Git, nor Eclipse. It is assumed that you have basic experience with these tools. + +[Go to the Quick Start tutorial](/qs/050-start.html) + +{: style='clear:both;' } + ## Base Tutorial ![Thumbnail for Base Tutorial](/img/tutorial_base/debug-xray-1.png) @@ -16,6 +29,24 @@ The Base Tutorial is a very extensive tutorial that takes you to all the princip {: style='clear:both;' } + +## A Maven ONLY Tutorial for an Eval Service + +![Thumbnail for Maven Tutorial](/tutorial_eval/img/tutorial_eval.png) +{: .thumb200-l } + +THIS IS IN BETA FOR NOW + +This tutorial mirrors the base tutorial but uses Maven instead of Bndtools. It +takes you to the process of creating a small web application with an expression +evaluator with nothing but mvn and vi. It shows how to do API based design, +it creates 2 providers, a Gogo command, and a web app. It also shows how to +do integration testing with maven. + +[Go to the Maven Eval Service Tutorial](/tutorial_eval/050-start.html) + +{: style='clear:both;' } + ## IoT Tutorial ![Thumbnail for IoT Tutorial](/img/tutorial_iot/exploring-led-breadboard-1.png) diff --git a/_config.yml b/_config.yml index ee2e89f..32cf2b1 100644 --- a/_config.yml +++ b/_config.yml @@ -13,6 +13,8 @@ collections: output: true book: output: true + qs: + output: true services: output: true trains: @@ -27,6 +29,8 @@ collections: output: true tutorial_wrap: output: true + tutorial_eval: + output: true tutorial_maven: output: true diff --git a/_data/sidebar.yml b/_data/sidebar.yml index 0df208a..350e0be 100644 --- a/_data/sidebar.yml +++ b/_data/sidebar.yml @@ -40,18 +40,18 @@ nav: - name: bnd manual url: "https://bnd.bndtools.org" external: true - - title: Knowledge + - title: enRoute classic links: - - name: App Notes - url: "/book/680-appnotes.html" + - name: About OSGi + url: "/book/210-doc.html" - name: Services url: "/book/400-services.html" - name: Tutorials url: "/book/150-tutorials.html" + - name: App Notes + url: "/book/680-appnotes.html" - name: Examples url: "/book/180-examples.html" - - name: About OSGi - url: "/book/210-doc.html" - title: Development links: - name: On Github diff --git a/_layouts/baselayout.html b/_layouts/baselayout.html new file mode 100644 index 0000000..bcc3398 --- /dev/null +++ b/_layouts/baselayout.html @@ -0,0 +1,52 @@ + + + {% include head.htm %} + + + {% include nav.htm %} + +
+ + +
+
+
+ + + + + + + + {{content}} + + + + +
+
+ + + +
+ + +
+ +
+ + + + +
+ + {% include footer.htm %} + + diff --git a/_layouts/default.html b/_layouts/default.html index 684f2d4..eb6a4f9 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -1,55 +1,9 @@ - - - {% include head.htm %} +--- +layout: baselayout +--- - - {% include nav.htm %} + +{% if page.since %}Since {{ page.since }}{% endif %} +

{{ page.title }}

-
- - -
-
-
- - - - - - - - {% if page.since %}Since {{ page.since }}{% endif %} -

{{ page.title }}

- - {{content}} - - - - -
-
- - - -
- - -
- -
- - - - -
- - {% include footer.htm %} - - +{{content}} \ No newline at end of file diff --git a/_layouts/prev-next-collection.html b/_layouts/prev-next-collection.html index 37b12b8..48a774f 100644 --- a/_layouts/prev-next-collection.html +++ b/_layouts/prev-next-collection.html @@ -1,38 +1,52 @@ --- -layout: default +layout: baselayout --- {% assign prev = nil %} {% assign next = nil %} +{% assign prevTitle = nil %} +{% assign nextTitle = nil %} {% assign in_loop = false %} -{% for service in site[page.collection] %} +{% for entry in site[page.collection] %} {% if in_loop %} - {% assign next = service.url %} + {% assign next = entry.url %} + {% assign nextTitle = entry.title %} {% break %} {% endif %} - {% if service.url == page.url %} + {% if entry.url == page.url %} {% assign in_loop = true %} {% continue %} {% endif %} {% if forloop.last == false %} - {% assign prev = service.url %} + {% assign prev = entry.url %} + {% assign prevTitle = entry.title %} {% endif %} {% endfor %} -{{ content }} -
-{% unless forloop.first %} + +{% capture navigation_links %} {% if prev %} - Prev + Prev {% endif %} -{% endunless %} -{% unless forloop.last %} {% if next %} - Next + Next {% endif %} -{% endunless %} \ No newline at end of file +{% endcapture %} + +{{navigation_links}} +
+ +{% if page.since %}Since {{ page.since }}{% endif %} +

{{ page.title }}

+ +{{content}} +
+ +{{navigation_links}} + + \ No newline at end of file diff --git a/_qs/050-start.md b/_qs/050-start.md new file mode 100644 index 0000000..4cdadc3 --- /dev/null +++ b/_qs/050-start.md @@ -0,0 +1,50 @@ +--- +title: Quick Start Tutorial +layout: prev-next-collection +lprev: /book/150-tutorials.html +lnext: /tutorial_base/050-start.html +version: 2.0.0 +noindex: true +--- + +![Thumbnail for Quickstart Tutorial](/img/qs/app-0.png) +{: .thumb200-l } + +In this quick start we develop a little project that, creates a single page web-application. + +This tutorial is light on the explanations because it focuses on introducing the overall architecture of enRoute, not the details. Over time this site will be filled with tutorials and documentation (or references to those) that will explain the minute details. This however, is about some big steps. + +We will cover the whole chain, from creating a workspace all the way to continuous integration. + +A disclaimer. This quick start is about learning to use OSGi enRoute, not about learning Java, Git, nor Eclipse. It is assumed that you have basic experience with these tools. + +If you have any questions about this quick-start, please discuss them in the [forum][forum]. + +## Sections + +
+
    + +{% for qs in site.qs %}{%unless qs.noindex%}
  1. {{qs.title}} – {{qs.summary}}
  2. +{%endunless%}{% endfor %} + +
+
+ + +## End + +So, you've finished this tutorial! What's next? + +Well, first, since we're still in beta, we'd love feedback. Our most favorite feedback is a pull request on the documentation. As an early user you must have run into some rough edges, outright stupidities, or you had a brilliant idea. Just go to the [OSGi enRoute][enroute-doc] repository on Github. Clone it in your own account, make your changes or additions, and send a pull request. We, and others like you, highly appreciate these kind of contributions. + +This tutorial is only a quick start, we've not explained a lot and just tried to get you on a starting level as soon as possible. To get more background, we highly recommend doing the [Base Tutorial](/tutorial_base/050-start.html). This tutorial follows the same route but shows how to design with service, the cornerstone of OSGi enRoute. + +You then might want to read the [Service Oriented Systems](/doc/215-sos.html) chapter how to build Service Oriented Systems. + +However, running into real problems is the best way to learn a technology. If you run into problems, use the [Forum][forum] to ask questions and get answers. + +And watch this space, we will expand this site with hundreds data sheets of services you can find on the net. These data-sheets will show you how to use this service in your application with real examples. + +[forum]: /forum.html +[enroute-doc]: https://github.com/osgi/osgi.enroute.site diff --git a/_qs/100-prerequisites.md b/_qs/100-prerequisites.md new file mode 100644 index 0000000..5f397df --- /dev/null +++ b/_qs/100-prerequisites.md @@ -0,0 +1,10 @@ +--- +title: Prerequisites +layout: prev-next-collection +lprev: 050-start.html +lnext: 200-workspace.html +summary: The prerequisites for the use of OSGi enRoute (Important!) +--- + +{% include prerequisites.md %} + diff --git a/_qs/200-workspace.md b/_qs/200-workspace.md new file mode 100644 index 0000000..6de2cef --- /dev/null +++ b/_qs/200-workspace.md @@ -0,0 +1,24 @@ +--- +title: The Workspace +layout: prev-next-collection +lprev: 100-prerequisites.html +lnext: 300-application.html +summary: Setup a bnd workspace +--- + +## What you will learn in this section +We will setup a bnd(tools) workspace for OSGi enRoute so we can build an application in the next section. + +Before you start this section, make sure you've checked the [prerequisites](100-prerequisites.html) for OSGi enRoute on your platform. + +{% include workspace.md %} + +### File System + +Since we made changes to your file system, a short summary of where we placed what. + +The Eclipse workspace was placed in a special place for Eclipse workspaces, the `~/eclipse` directory. We named this workspace `com.acme.prime`, which is a good name. The bnd workspace was placed also in your home directory, in the `~/git` directory, also under the name `com.acme.prime`; using the same name for both the Eclipse and bnd workspaces is a good practice. + +## Next + +In the next section of this quick start tutorial we will create a sample application. diff --git a/_qs/300-application.md b/_qs/300-application.md new file mode 100644 index 0000000..5cd0275 --- /dev/null +++ b/_qs/300-application.md @@ -0,0 +1,210 @@ +--- +title: Creating an Application +layout: prev-next-collection +lprev: 200-workspace.html +lnext: 410-exercise-service.html +summary: Creating a sample enRoute Application +--- + +## What To Do +In the previous section we created a fresh clean OSGi enRoute workspace in `~/git/com.acme.prime` and selected the `Bndtools` perspective. In this section we're going to create an application project in this workspace that will run inside an OSGi framework. This 'application' will provide a web user interface based on Google's [Angular JS](https://angularjs.org/) and Twitter's [Bootstrap](http://getbootstrap.com/). We will use the OSGi enRoute built-in template since this is setup to provide exactly that (coincidence of course!). + +## Create bndtools Project +So let's get started by creating a new Bndtools Project. Select `File/New/Bndtools OSGi Project`. + +Over time the menu system can have small changes. Variations seen are `Bnd OSGi Project` and `Bndtools Project`. +{: .warning } + + +![Create Application Project](/img/qs/app-create-0.png) + +This will open a wizard where we select the _template_. For this tutorial, it is mandatory to use the OSGi enRoute template since our workspace is not setup for the other templates. The OSGi enRoute templates create specific project types based on the suffix of the project name. In this case we create an application project. + +![Create Application Project](/img/qs/app-create-1.png) + +Now naming is important and we've found that using Java package like names that use the workspace name as a prefix works best for projects. So we pick `com.acme.prime.upper.application`. For OSGi enRoute, this `.application` suffix is *crucial* since it defines the template we will use. So in the first page we enter this name. + +![Select the OSGi enRoute template](/img/qs/app-create-2.png) + +Select `Next` to go to the Java settings page, which should not change since OSGi enRoute has already set this up. + +![Select the OSGi enRoute template](/img/qs/app-create-3.png) + +So we can just click `Finish` and get it over with. + +## Code + +The OSGi enRoute template has already created some source code for us. This source code is making a single page web-application. So double click on the `UpperApplication.java` source file to open the Java editor to see what kind of code we need. + +![The UpperApplication source code](/img/qs/upper-0.png) + +So what's in there? The first thing you will see is a number of annotations. They ensure that we include the proper web resources for our application like Angular, Bootstrap, and the web extender that serves our static pages. Then we have the component annotation that makes this object a Declarative Services _service component_. A service component is automatically registered as a service when it implements an interface and it can depend on other services. + +This `UpperApplication` component implements the _REST_ interface and is thus registered as a _REST_ service. The contract of this service indicates that any public method in this class becomes available as a REST end-point. The `getUpper` method is for the `GET` method (duh, it starts with `get`. If you want a `POST` call it `postUpper`) and it is mapped from the `/rest/upper` URI. Since it accepts a single argument, we can specify the word we want to upper case as `/rest/upper/`. You can find more information about the REST API in the [service catalog](/services/osgi.enroute.rest.api.html) + +REST methods are called from an untrusted external source so they should be protected by a check for authorization. +{: .warning} + +The OSGi enRoute REST support is intended for calling your app from a browser. If you must implement +a complex REST API then it might be better to use JAX-RS. The OSGi is in the process of standardizing +a service API for JAX-RS. +{: .note} + + +## HTML Resources + +Since this is a single page web app we also need some static resources for the Javascript code and CSS. + +The resources from this application are stored in the `static` directory which is included in our bundle. These resources are directly mapped to the root. That is, a resource with the path `static/abc/def` will be available as `/abc/def`. The recommendation is to create a static direction with the application PID name in `static`. + + static/ + com.acme.prime.upper + index.html + ... + +The `static/com.acme.prime.upper/index.html` contains the single page HTML root. It defines a header, view area, and a footer. The `com.acme.prime.upper/main/htm` directory contains _html fragments_ that are inserted in the main page depending on the URI. Take a look at these resources and notice how these resources can use macros from the build environment. + +## Automatic Resources + +In the Java code we require several Javascript resources and CSS resources. If you look in the `index.html` file you see entries like: + + + + + +OSGi enRoute will automatically insert any CSS or Javascript code in these places that your bundle requires through the annotation. Additionally, at the end it will add any such code in your bundle's `web` directory. + +We won't go into more detail now because we just want to see it run! Understanding is a lot easier when you see it all run. + +## Defining a Runtime + +Double click on the `com.acme.prime.upper.bndrun` file and select the `Run` tab. In this tab we can express the requirements we have on the runtime. Since we specified our requirements via the annotations, we're good to go as long as our application is listed in the initial requirements. This is the case by default. You could add any of the other bundles listed on the left side as a requirement but let's assume we're good for now. + +![Runtime Requirements](/img/qs/resolve-initial-0.png) + +So hit the `Resolve` button. This will open a dialog that shows you what bundles are required in runtime. + +![Resolved set](/img/qs/resolve-initial-1.png) + +Clicking `Finish` will set the `Run Bundles` list. This list is normally not visible. Open it if you'd like to see the resulting bundles. + +![Resolved set](/img/qs/resolve-initial-2.png) + +Note that every time you resolve, the `-runbundles` are overwritten with the new resolution. So **never** add bundles directly to the `-runbundles` if you use the resolver! + +Save the `com.acme.prime.upper.bndrun` file and then click on the `Debug OSGi` button at the right top of the window. + +![Resolved set](/img/qs/run-buttons-0.png) + +Your app is running and waiting for customers to enjoy the terrific upper casing: + +[http://localhost:8080/com.acme.prime.upper](http://localhost:8080/com.acme.prime.upper)! + +Just click on the 'To Upper!' button. This will ask you for a word and then prints the result in the alert bar. + +![The Application](/img/qs/app-0.png) + +## Debugging + +Of course you will never need to debug OSGi enRoute projects since they are by definition perfect! However, since perfection isn't what it used to be, let's see how we can do some debugging in this project. + +You can debug this project as you can any other project in Java. You can set breakpoints and single step. There is one difference with more traditional Java. In our case, we generate a bundle that gets deployed on every change we make. If you change some code and save it, a new bundle will get deployed. If you get more requirements in the `bndrun` file, those new bundles will be deployed or no longer necessary bundles get removed. This works so well that the dialog box that Eclipse sometimes pops up to tell you it could not patch the class files can be ignored because bnd does not rely on this patching. + +![Resolved set](/img/qs/debug-patch-0.png) + +So just click the check-box and dismiss this dialog. That out of the way, let's change our code from making this upper case code to return lower case code. (Don't kill the running framework.) + + public String getUpper(RESTRequest req, String string) throws Exception { + return string.toLowerCase(); + } + +If there are Javascript or html fragment changes, you need to refresh the page in the browser to reload. Otherwise you can just click the button on your browser and try it out. You actually rarely have to restart the framework. + +## OSGi Details + +We're running in a framework but there is not much to see of the framework yet. Obviously, we will need tools to see what bundles are running and what services are registered. Well, every application project has a basic `bndrun` file and a `debug.bndrun` file. The `debug.bndrun` file inherits from the basic one but it adds a lot of support to look inside the framework. + +Let's first kill our running framework. Just click the red button on the console view. Also, if you've done some debugging, you might want to return to the Bndtools perspective. + +Then double click the `debug.bndrun` file and select the `Run` tab, then click on the `Resolve` button. This gives us a much larger list of bundles. The debug enRoute settings add Web Console, XRay, a web server etc. These are invaluable tools. + +This window looks similar to the following picture. Note that there are no listed requirements because they are inherited from the `com.acme.prime.upper.bndrun` file. + + +![Resolved set](/img/qs/debug-details-0.png) + +So save the `debug.bndrun` file and click `Debug OSGi`. First, this `bndrun` file will run in trace mode. (You can control this through the `-runtrace` property that you can see when you double click the `debug.bndrun` file and select the `Source` tab.) In trace mode, the launcher provides detailed information about the launch process as well the ongoing update process when there are changed in bndtools. + +Anyway, we now have the unsurpassed [Web Console](http://felix.apache.org/documentation/subprojects/apache-felix-web-console.html) running with XRay. Just click on [http://localhost:8080/system/console/xray](http://localhost:8080/system/console/xray). + +If you're asked for your credentials, the Apache Felix boys have given you an unforgettable user id & password: + + User id: admin + Password: admin + +![Resolved set](/img/qs/debug-xray-0.png) + +In the full tutorial the possibilities of XRay are further explained. + +## Creating an Executable + +The last part of this quick start is creating an executable JAR out of our application. The export facility of bnd(tools) makes it possible to create a JAR that contains all the dependencies, including the launcher and the framework. This is sometimes hard to understand for enterprise developer, that Java can actually run outside an application server! + +Double click on the `com.acme.prime.upper.bndrun` file and select the `Run` tab. + +At the top of this window you see the following buttons: + +![Run buttons](/img/qs/run-buttons-0.png) + +The `Export` button creates an executable JAR out of the specification of its corresponding `bndrun` file. The execution will be identical to when you run your code inside Eclipse. So click on the `Export` button: + +![Resolved set](/img/qs/export-0.png) + +Click on `Next` to go to the wizard page that requests for the path to save the executable JAR at. Suggest you save it on the desktop under the name `com.acme.prime.upper.jar`: + +![Resolved set](/img/qs/export-1.png) + +Then we click `Finish`. + +Make sure you have no more frameworks running. Since we have a webserver running we easily run in a conflict for the 8080 port number. + +Let's go to a shell to see if we can execute our code. + + $ cd ~/Desktop + $ java -version + java version "1.8.0" + Java(TM) SE Runtime Environment (build 1.8.0-b132) + Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode) + $ java -jar com.acme.prime.upper.jar +{: .shell} + +We can now go to [http://localhost:8080/com.acme.prime.upper](http://localhost:8080/com.acme.prime.upper) and see that our application also runs from the command line. + +You can do Control-C in the shell to exit. + + $ java -jar com.acme.prime.upper.jar + ^C + $ +{: .shell} + + + + + + + + + + + + + + + + + diff --git a/_qs/410-exercise-service.md b/_qs/410-exercise-service.md new file mode 100644 index 0000000..8794fab --- /dev/null +++ b/_qs/410-exercise-service.md @@ -0,0 +1,77 @@ +--- +title: Exercise Services +layout: prev-next-collection +lprev: 300-application.html +lnext: 050-start.html#End +summary: Using services in the Demo Application +--- + +## Using a Service + +Currently we make a mortal error for Service Oriented Systems; we mix different types of functionality. The purpose of the `UpperApplication` class is to be a facade to the underlying services; it is very wrong to actually provide functionality in such a facade; its only purpose is to protect against outside evil and dispatch to the underlying (unprotected) service. + +So in this exercise you should create a service API and a provider. From a high level, this will consists of the following steps: + +* Create an API. We will create an API project that exports an `Upper` interface to turn a string into upper case. +* Create a provider for the Upper service. +* Convert the `UpperApplication` class to use the Upper service. +* Add the provider to the set of run bundles + +### Create an API + +It is important to keep API code away from implementation code. If you store API and implementation in the same project it is too easy to leak implementation code to other projects that only require the API. + +In OSGi enRoute this means the name of the project should end with `.api` and you should select the OSGi enRoute template. Let's call our general API project `com.acme.prime.api`. Why not `com.acme.prime.upper.api`? Well, we plan for the future; we want to reuse this project for other API as well. We could create a project per API but that tends to create hundreds of almost empty projects over time. Since a workspace should be a cohesive module, the APIs we define in this general API project are very likely to be also cohesive. + +In this new project, we rename the template API to reflect our semantics. So rename the `com.acme.prime.api` package to `com.acme.prime.upper.api` and the `Prime.java` file to `Upper.java`. Then change the `Upper` class so that we can use it to change a word to upper case: + + public interface Upper { + String upper(String input); + } + +If you have another API in the future, you can then add it to the same project in another package. + +### Create a Provider + +Now create a provider project for the Upper API. Call the project `com.acme.prime.upper.provider` and use the OSGi enRoute template. By default, this project does not see the API project so we should add it. This is done by double clicking on the `bnd.bnd` file in the `com.acme.prime.upper.provider` project and selecting the `Build` tab. On this tab, select the `+` and add the API project (double clicking works). This is added as `latest`, meaning that we use the version from the workspace. + +We then should change the `UpperImpl` class to implement the `Upper` interface from our API project, so we must import `com.acme.prime.upper.api.Upper`. This class already is marked as a _component_ so that it registers its implemented interface as a service. + + @Component(name = "com.acme.prime.upper") + public class UpperImpl implements Upper { + public String upper(String input) { + return input.toUpperCase(); + } + } + +In general, a provider bundle should export the API it _provides_; in our case we provide the contract specified in the `com.acme.prime.upper.api` package. Exporting this package is a highly recommended best practice, it makes a lot of things work better later on. You can export this package by selecting the `bnd.bnd` file in the `com.acme.prime.upper.provider` project, and then the `Contents` tab. Notice the import: `com.acme.prime.upper.api`. Drag this import to the export list and save the file. The imports now disappears. + +### Change the Upper Application + +We now need to change the `UpperApplication` class to use our new incredibly powerful service. Currently it has no dependencies so we should add the dependency on the Upper service. + +The first thing we need to do is to make sure the Upper Application can see the API project. So click on the `bnd.bnd` file, select the `Build` tab, and add the API project, just like we did in the provider project. + +Then we change the `UpperApplication` component class. We must add a setter method for the `Upper` service with a `@Reference` annotation, which means we must import `org.osgi.service.component.annotations.Reference`. No we can add the reference to the end of the class (convention is to place references at the end): + + public class UpperApplication implements REST { + ... + + @Reference + Upper upper; + } + +The `@Reference` annotation creates a dependency on this service; the `UpperApplication` component is not started until the service registry contains an Upper service. + +The next step is to use the `upper` instance variable that we've just set in the `getUpper` method (so we must import `com.acme.prime.upper.api.Upper` again). + + public String getUpper(String string) { + return upper.upper(string); + } + +If the OSGi framework is still running, you likely get errors since we now have an unresolved requirement in our code; we're referring to the `com.acme.prime.upper.api` package which is now not provided by anybody. + +### Add the Provider to the Set of Run Bundles + +We've changed our dependencies so we need to re-resolve our run bundles. So go to the `com.acme.prime.upper.application` project and select the `com.acme.prime.upper.bndrun` file by double clicking, select the `Run` tab. Hit the `Resolve` button and then verify that the `com.acme.prime.upper.provider` has been added. Save the `com.acme.prime.upper.bndrun` file. This should automatically deploy the set of run bundles in the running framework. Just refresh the browser and check if it still works! If not, well, then your intermediate exercise is to debug it :-) + diff --git a/_tutorial_eval/050-start.md b/_tutorial_eval/050-start.md new file mode 100644 index 0000000..a633dcf --- /dev/null +++ b/_tutorial_eval/050-start.md @@ -0,0 +1,93 @@ +--- +title: Tutorial Using OSGi enRoute with Maven +layout: prev-next-collection +summary: Shows how to build an OSGi enRoute program using Maven only +lprev: /book/150-tutorials +lnext: 100-parent +version: 2.0.0 +noindex: true +--- + +![Maven Eval Tutorial](/tutorial_eval/img/tutorial_eval.png) +{: .thumb200-l } + +This tutorial shows you how to build an OSGi enRoute +executable JAR using Maven and vi only. This tutorial will be purely command-line based. (There is one +section outlining how M2E and the Bndtools editors can be useful, even for pure command line +aficionados.) + +What we will build is a similar application that is developed in the [Base Tutorial] but instead of Bndtools we use Maven. + +This application is a trivial Web application. It provides a text input box and an `Eval` button. The +text is sent to the server for evaluation. There a service is used to evaluate the input. We'll +turn the application into an executable JAR that contains all its dependencies. + +The tutorial will only use plain Maven and vi. You can of course use your own favorite +editor to edit the pom and bnd files. + +The result of this tutorial can be found on Github at +[https://github.com/osgi/osgi.enroute.examples.eval](https://github.com/osgi/osgi.enroute.examples.eval). + +A disclaimer. This tutorial is about learning to use Maven to build an OSGi enRoute +executable JAR, it is not about learning vi, Java, Git, nor Maven or M2Eclipse. + +And as always, [pull requests][osgi.enroute.site] are highly appreciated. + +## What We Will Build + +This tutorial will build the following application: + +![Application in bundles](img/m2e-bundles.png) +{: width=50%} + +You can find more about this form of documenting in the [About OSGi](/doc/100-about-osgi) section + +## Where + + +You should create a directory somewhere in your file system, let's say `~/workspaces/osgi.enroute.examples.eval`. +We are going to assume this is your default directory for your shell. + + $ mkdir ~/workspaces/osgi.enroute.examples.eval + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + + +## Sections + +
+
    + +{% for t in site.tutorial_eval %}{%unless t.noindex%}
  1. {{t.title}} – {{t.summary}}
  2. +{%endunless%}{% endfor %} + +
+
+ + +## End + +So, you've finished this tutorial! What's next? + +We'd love some feedback. Our most favorite feedback is a pull request on the documentation. +As an early user you must have run into some rough edges, outright stupidities, or you had +a brilliant idea. Just go to the [OSGi enRoute][osgi.enroute.site] repository on Github. +Clone it in your own account, make your changes or additions, and send a pull request. +We, and others like you, highly appreciate these kind of contributions. + +After you've done this tutorial you should have a basic feeling of how to build an +application using Distributed OSGi with OSGi enRoute. So the best way to continue +learning is to build a small application based on these principles. Running into +real problems is the best way to learn a technology. If you run into problems, use +the [Forum][forum] to ask questions and get answers. + +And watch this space, we will expand this site with many data sheets of +services you can find on the net. These data-sheets will show you how to +use this service in your application with real examples. + +[forum]: /forum.html +[osgi.enroute.site]: https://github.com/osgi/osgi.enroute.site +[Quick Start Tutorial]: /qs/050-start +[Base Tutorial]: /tutorial_base/05_start +[M2Eclipse]: http://www.eclipse.org/m2e/ +[Bndtools]: http://bndtools.org/ diff --git a/_tutorial_eval/100-parent.md b/_tutorial_eval/100-parent.md new file mode 100644 index 0000000..1b9c255 --- /dev/null +++ b/_tutorial_eval/100-parent.md @@ -0,0 +1,201 @@ +--- +title: Parent Pom +layout: prev-next-collection +summary: Create a Maven parent pom project +lprev: 050-start +lnext: 300-api +--- + +## What you will learn in this section + +Maven supports the concept of a _parent_ pom. This pom collects the information +that is shared between a number of projects. + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + +## Directory Layout + +The directory layout for our complete project will be: + + ./ + osgi.enroute.examples.eval/ + api/ + application/ + bndrun/ + command/ + integration-test/ + parsii.provider/ + simple.provider/ + test/ + +We will create this layout along the way step by step. + +## Creating the Parent Pom + +In the default directory (`~/osgi.enroute.examples.eval/`) we create a `pom.xml` +file. It has the following content. + + osgi.enroute.examples.eval $ vi pom.xml + // fill the content from the next sections +{: .shell } + + +First the mandatory prolog: + + + + 4.0.0 + +In this section we define the default group name for any modules falling under this +module project. The `groupId` for the projects will be `org.osgi` and the `artifactId`, which +will also be the Bundle Symbolic Name for OSGi, will follow the pattern +`osgi.enroute.examples.eval.*`. + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + +Module POMs have no output and should have packaging set to POM. + + pom + +Pretty standard Maven setup for our defaults. + + + UTF-8 + + + + + +Compiler setup: + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + +Standard plugin to jar-up the artifact: + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.1 + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + +The bnd plugin that will calculate our manifest and other files in the bundle. This +plugin will automatically run in m2e to do a lot of magic for you. + + + biz.aQute.bnd + bnd-maven-plugin + 3.3.0 + + + + bnd-process + + + + + + + + +The important part, the dependencies: + + + +This is a compile dependency providing us with the standardized set of functions +in OSGi enRoute. This dependency contains no runtime dependencies; it is specification +only. + + + org.osgi + osgi.enroute.base.api + 2.0.0 + + +We are adding JUnit because we will use this to test our code: + + + junit + junit + 4.12 + + + + +The following repositories are defined if you want to use the OSGi enRoute and bnd +snapshot bundles and/or plugins. + + + + osgi-snapshots + https://oss.sonatype.org/content/groups/osgi/ + default + + + bnd-snapshots + https://bndtools.ci.cloudbees.com/job/bnd.master/lastSuccessfulBuild/artifact/dist/bundles/ + default + + + + + + bnd-snapshots + https://bndtools.ci.cloudbees.com/job/bnd.master/lastSuccessfulBuild/artifact/dist/bundles/ + default + + true + + + + + + +## Verify + +After you created the pom.xml file you should verify that it all is ok. + + osgi.enroute.examples.eval $ mvn verify + [INFO] Scanning for projects... + [INFO] + [INFO] ------------------------------------------------------------------------ + [INFO] Building osgi.enroute.examples.eval 1.0.0-SNAPSHOT + [INFO] ------------------------------------------------------------------------ + [INFO] + [INFO] --- bnd-maven-plugin:3.3.0:bnd-process (default) @ osgi.enroute.examples.eval --- + [INFO] skip project with packaging=pom + [INFO] + [INFO] --- maven-install-plugin:2.4:install (default-install) @ osgi.enroute.examples.eval --- + [INFO] Installing /Ws/enroute/osgi.enroute.examples.eval/pom.xml to /Users/aqute/.m2/repository/org/osgi/osgi.enroute.examples.eval/1.0.0-SNAPSHOT/osgi.enroute.examples.eval-1.0.0-SNAPSHOT.pom + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 0.327 s + [INFO] Finished at: 2016-10-04T16:39:25+02:00 + [INFO] Final Memory: 9M/309M + [INFO] ------------------------------------------------------------------------ +{: .shell } + +Since the output of mvn is quite, lets say, verbose, we will only show relevant parts in the +following sections. Any skipped parts will be indicated with `...`. + diff --git a/_tutorial_eval/300-api.md b/_tutorial_eval/300-api.md new file mode 100644 index 0000000..8b74fd9 --- /dev/null +++ b/_tutorial_eval/300-api.md @@ -0,0 +1,303 @@ +--- +title: API Project +layout: prev-next-collection +lprev: 100-parent +lnext: 350-provider +summary: How to work with bnd projects properly, API based design. +--- + +## What you will learn in this section +In this section we are going to create an API for a simple expression evaluator. +It will teach you how to create an API project, how to name a project, and how to +navigate around in the project. We also explain how to version packages. + +![API Diagram](img/api-diagram.png) + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + +## Creating a POM + +You should create a directory `api` in the `osgi.enroute.examples.eval` directory. In this +directory we create a `pom.xml`. + + osgi.enroute.examples.eval $ mkdir api + osgi.enroute.examples.eval $ cd api + api $ vi pom.xml + // fill in the content from the next sections +{: .shell } + +This POM should look like: + + + 4.0.0 + +Define the parent pom, we inherit lots of information from +that POM. + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + + +We inherit the version and group Id from our parent POM so we only have to specify +the artifact Id. The artifact Id will also act as the Bundle Symbolic Name. In OSGi enRoute +the following (highly recommended) conventions are advised for the last segment of the +artifact Id name: + +* `.api` – API only project +* `.provider`, `adapter` – An implementation project +* `.application` – An application project. This is a project that binds together a set of components and parameterizes them. +* `.test` – An OSGi test project, tests are run inside a framework. + +The name of the bundle + + osgi.enroute.examples.eval.api + +This bundle generates a JAR that contains the API code with the OSGi metadata. We therefore need +to package it as a JAR. + + jar + +Documentation is always desired: + + Eval API + + + +Verify your pom by running `mvn verify`. + + api $ mvn verify + ... + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + ... +{: .shell } + +## The Source + +We want to make a component that evaluates expressions. We first define the contract. +The service contract for now could be an interface. Define the following in the +`src/main/java/osgi/enroute/examples/eval/Eval.java` file in the `osgi.enroute.examples.eval/api` directory. + + api $ mkdir -p src/main/java/osgi/enroute/examples/eval/api + api $ vi src/main/java/osgi/enroute/examples/eval/api/Eval.java + // fill in the content from the next section +{: .shell } + + package osgi.enroute.examples.eval.api; + /** + * A service that evaluates an expression and returns the result + */ + + public interface Eval { + /** + * Evaluate an expression and return the result. + */ + double eval(String expression) throws Exception; + } + + +## Versioning + +In OSGi packages that are shared between bundles have a _version_. Though this +sounds horrendously complex for a developer that are used to the Maven version +chores, rest assured. The bnd maven plugin significantly simplifies version handling. + +We therefore need to put a file in the package directory that contains the version. +We use an annotation on the package for this. Therefore, create a file `package-info.java` +with the following content in the package directory: + + api $ vi src/main/java/osgi/enroute/examples/eval/api/package-info.java + // fill in the content from the next section +{: .shell } + + @org.osgi.annotation.versioning.Version("1.0.0") + package osgi.enroute.examples.eval.api; + +The bnd plugin will pickup this version and add it to the manifest when the +package is exported. You should change the version when you make changes to the +package content. + +## Semantic Versioning + +The OSGi strongly recommends _semantic versioning_. The rules for semantic versioning +prescribe what part of the version to change based on compatibility. Semantic version +is very important for package version and you should strictly follow them. (There is +a plugin that can verify when you violate the rules based on Java's binary compatibility +model.) + +A version consists of 4 parts in OSGi: + +* Major – The first number. If it is changed between releases it indicates that it is incompatible with the previous release. +* Minor – The second number. If it is changed between releases it indicates that _providers_ are broken but _consumers_ are compatible. +* Micro – The third number. If it is changed between releases it means that there is no binary nor semantic change, for example an improved comment. +* Qualifier – The last segment. This is normally changed between builds. + +### Provider & Consumer Types + +In this API, any party that will implement the `Eval` interface is considered to be +the _provider_. A provider must fully implement a contract that has virtually no +backward compatibility unlike _consumers_ of this API. Any change in the version +that affects the public API must result in rebuild of the provider's bundle. That +is, if our version here goes to 1.1 we want to make sure our providers that implemented +1.0 are no longer compatible. + +Obviously it is a nightmare to ensure that the proper version ranges are used. We can +significantly help the provider by adding an annotation to this interface: + + import org.osgi.annotation.versioning.ProviderType; + + @ProviderType + public interface Eval { ... } + +The bnd tool will now automatically ensure that any _implementers_ of this interface +use semantic versioning to import the package with a minor range, for example `[1.0,1.1)` +because they are deemed a provider. + +Interfaces that are implemented by the consumer of your API (usually listener like interfaces) +can be annotated with the `@ConsumerType` annotation. However, this is the default. + +If you find this hard to grasp then you're not alone. This is a very complex area. We +will get back to this later so don't worry if you do not immediately grasp it. + +## The bnd File + +The bnd plugin provides the OSGi metadata and keeps us honest. The plugin, defined in the parent POM, +requires a bnd.bnd file in the same directory as the POM. In our case, we provide +the API we should therefore export the package. + + api $ vi bnd.bnd + // fill in content from next section +{: .shell } + + # + # OSGi enRoute Examples Eval API Project + # + + Bundle-Description: \ + An API for an expression parser + + Require-Capability: \ + compile-only + + Export-Package: osgi.enroute.examples.eval.api + +A `bnd.bnd` file is a properties file, same rules apply. Comments can be added +by starting a line with a hash mark (`#`). The first word on the line is the key and +then a space, a ':' or '=' marks the separator. The rest is the value minus any leading +spaces. + +Keys that start with an upper case character are added to the manifest, other +keys are macros and can be used anywhere with the `${key}` syntax. + +The Bundle-Description key should be self describing. + +### Compile Only + +If you look in the `bnd.bnd` file you see that this API bundle is compile only: + + Require-Capability: \ + compile-only + +The reason this is compile only is that best practices dictate that the provider +should export its API. There have been numerous discussions about this and some opinions +differ. However, a provider of an API is extremely closely tied to the version of the +API it provides. Virtually any change in the API requires a change in the provider, +there is no backward compatibility as there is for the API consumers. Therefore, by +exporting the API from the provider you make the whole system less complex. + +### Export + +All packages in the project are automatically added to the bundle. By default, these packages are private. +The Export-Package header tells bnd to add the export header for the `osgi.enroute.examples.eval.api` package. + + +## Building + +Our `osgi.enroute.examples.eval/api` directory should look as follows: + + bnd.bnd + pom.xml + src/main/java/osgi/enroute/examples/eval/api/package-info.java + src/main/java/osgi/enroute/examples/eval/api/Eval.java + +You might have a `target` directory, this is where maven stores temporary files. To update/create the +target we run `mvn install` again. + + api $ mvn install + ... + api $ ls -1 target + classes + generated-sources + maven-archiver + maven-status + osgi.enroute.examples.eval.api-1.0.0-SNAPSHOT.jar +{: .shell } + +There are some additional files in the target directory. Since we did an install, we also copied the JAR to +the Maven local repository in `~/.m2/repository/org/osgi/osgi.enroute.examples.eval.api/1.0.0-SNAPSHOT/osgi.enroute.examples.eval.api-1.0.0-SNAPSHOT.jar`. + + api $ ls ~/.m2/repository/org/osgi/osgi.enroute.examples.eval.api/1.0.0-SNAPSHOT/osgi.enroute.examples.eval.api-1.0.0-SNAPSHOT.jar + ~/.m2/repository/org/osgi/osgi.enroute.examples.eval.api/1.0.0-SNAPSHOT/osgi.enroute.examples.eval.api-1.0.0-SNAPSHOT.jar +{: .shell } + +If we have bnd installed as command line tool then you can take a look at the manifest: + + $ bnd print target/osgi.enroute.examples.eval.api-1.0.0-SNAPSHOT.jar + [MANIFEST osgi.enroute.examples.eval.api-1.0.0-SNAPSHOT] + Bnd-LastModified 1474982517123 + Build-Jdk 1.8.0_25 + Built-By aqute + Bundle-Description An API for an expression parser + Bundle-ManifestVersion 2 + Bundle-Name osgi.enroute.examples.eval.api + Bundle-SymbolicName osgi.enroute.examples.eval.api + Bundle-Version 1.0.0.201609271321 + Created-By 1.8.0_25 (Oracle Corporation) + Export-Package osgi.enroute.examples.eval.api;version="1.0.0" + Manifest-Version 1.0 + Require-Capability compile-only,osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))" + Tool Bnd-3.3.0.201609221906 + + [IMPEXP] + Export-Package + osgi.enroute.examples.eval.api {version=1.0.0} +{: .shell } + +You can printout more by using the print options that you can see with `bnd help print`. + +## How does it Work? + +When we run maven the bnd plugin will analyze the class files and use the information +that is inside these files, which includes the annotations, to generate a manifest. The +bnd tool has a lot of OSGi knowledge embedded and it can find many errors and warnings +to signal ideas that are not such a good idea. + +The plugin places the manifest and other files in the `target/classes` directory. This +directory is then made into a JAR by the Maven jar plugin. + +In M2Eclipse, the bnd plugin runs after every change but the JAR plugin does not. So the classes +directory is updated but not the JAR. You need to run `maven install` to create the JAR in +the local repository. + + +## What Have We Done? + +You've just created your first bundle with maven! We've created a service API bundle that +we can use in other projects to compile against but it cannot be used in +runtime. + +We spent some effort on learning the semantic versioning rules and how to provide +package versions in OSGi. + + + diff --git a/_tutorial_eval/350-provider.md b/_tutorial_eval/350-provider.md new file mode 100644 index 0000000..5cf3d54 --- /dev/null +++ b/_tutorial_eval/350-provider.md @@ -0,0 +1,292 @@ +--- +title: Provider Bundle Project +layout: prev-next-collection +lprev: 300-api +lnext: 360-provider-junit +summary: Create a project that provides an implementation of the Eval service. +--- + +## What You will Learn in this Section + +In this section we create another project that provides an implementation for our +Eval API, a so called _provider_. In this case we call this the `simple.provider`. +We will also export the api package from the +provider bundle and explain why this will be simplifying your life. + +![API Diagram](img/simple-diagram.png) + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + +## About Services + +A _service_ is an object that is registered by a _bundle_. It is registered under +an interface and a set of properties. Services are dynamic which means that we +have to write code that uses services defensively. Since this a lot of +work we use [Declarative Services] to simplify this considerably with annotations. + +## Create a Provider Project + +In the previous section we created an API for an expression evaluator. We now need a +so called *provider* for this service. A provider is responsible for the contract +defined in the API so that *consumers* can use the service. In the case of the +Eval service the consumer will call the `eval(String)` method and the provider must +implement this method. + +In the `osgi.enroute.examples.eval` directory we create a directory called `simple.provider`. +In this directory we create the POM. + + osgi.enroute.examples.eval $ mkdir simple.provider + osgi.enroute.examples.eval $ cd simple.provider + simple.provider $ +{: .shell } + +## POM + + simple.provider $ vi pom.xml + // fill in from next section +{: .shell } + + + 4.0.0 + +The parent and packaging is the same as for the API project: + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + + + jar + +In the previous section we also learned that the last segment in the +project name defines its *type*; this is supported by OSGi enRoute templates. A provider +project must therefore have a name that ends in `.provider`. We also like to start +the project name with the workspace, the service API name and an indication of what +kind of implementation this is. Well, this is going to be an awful simple implementation, +so the name should be: `osgi.enroute.examples.eval.simple.provider`. So in the POM we should have: + + osgi.enroute.examples.eval.simple.provider + Eval Provider + +We inherit the OSGi enRoute base API (a collection of standardized and specialized service contracts to +make it easy for web apps) but in this case we need the API project as dependency + + + + org.osgi + osgi.enroute.examples.eval.api + 1.0.0-SNAPSHOT + + + + + +## Implementation Source + +We now need to add an implementation class. In this case we use regular expressions as the parser to +implement a very simplistic evaluation parser in the file `src/main/java/osgi/enroute/examples/eval/provider/EvalImpl.java`: + + simple.provider $ mkdir -p src/main/java/osgi/enroute/examples/eval/provider + simple.provider $ vi src/main/java/osgi/enroute/examples/eval/provider/EvalImpl.java +{: .shell } + + package osgi.enroute.examples.eval.provider; + + import java.util.regex.Matcher; + import java.util.regex.Pattern; + + import org.osgi.service.component.annotations.Component; + import org.osgi.service.component.annotations.Reference; + import org.osgi.service.log.LogService; + + import osgi.enroute.examples.eval.api.Eval; + + @Component(name = "osgi.enroute.examples.eval.provider") + public class EvalImpl implements Eval { + Pattern EXPR = Pattern.compile( "\\s*(?\\d+)\\s*(?\\+|-)\\s*(?\\d+)\\s*"); + + @Reference + LogService log; + + + @Override + public double eval(String expression) throws Exception { + Matcher m = EXPR.matcher(expression); + if ( !m.matches()) { + log.log(LogService.LOG_WARNING, "Invalid expression " + expression); + throw new IllegalArgumentException("Invalid expression " + expression); + } + + double left = Double.valueOf( m.group("left")); + double right = Double.valueOf( m.group("right")); + switch( m.group("op")) { + case "+": return left + right; + case "-": return left - right; + } + return Double.NaN; + } + } + +Ok, ok, simple might still give it too much credit but we're not here to learn parsing. +At least it has (some) error handling! Notice that we can only handle trivial additions and +subtractions of constants. + +This class is setup as a Declarative Service (DS) µservice component because the `@Component` +annotation was added. If your component class implements one or more interfaces, then these +will be automatically registered as OSGi services. So in this case, we want to implement +an `Eval` interface so that we're registered as an Eval service. + +We can use the `Eval` interface because we added the `org.osgi:osgi.enroute.examples.eval.api:1.0.0-SNAPSHOT` project +to our dependencies. + +## The bnd File + +To make a proper bundle we need to have a `bnd.bnd` file in the same directory as our POM. This file looks +like: + + simple.provider $ vi bnd.bnd + // fill in content from next section +{: .shell } + + # + # OSGi enRoute Eval Example + # + + Bundle-Description: \ + Provides a simple implementation for an eval parser + + Export-Package: osgi.enroute.examples.eval.api + +## Exporting API + +Observant readers would have noticed that the the `osgi.enroute.examples.eval.api` package is +exported but it is not part of this project. This is one of the magic, and extremely useful, +tricks of bnd. Any package listed in Export-Package or Private-Package headers will be copied +from the classpath even if not part of the project. + +However, this raises the question why should the API be included in the bundle? Should the +API not be a separate bundle? + +The reason we export the API from the provider bundle is that it makes life a lot easier +when we assemble the executable and it has no hidden cost. The primary reason it has no +hidden cost is due to _compatibility_. A provider has virtually no backward compatibility +with its API as was explained in the semantic versioning section. Any change in the +API will require an update of the provider code to ensure the provider implements any +additional obligations of the contract. + +Therefore, separating the API from the provider has no use since a given provider +will virtually always use the same API version. Carrying this version of the API +causes fewer bundles and less error prone metadata. + +There are OSGi experts who do not completely agree though. + +## Build + +In Maven-terms we've now defined a multi-module project, currently containing two modules : api and simple.provider. +The modules should be defined in an aggregator pom, and often this is done in the parent pom. +We should add a section in the pom.xml in the parent folder + + simple.provider $ cd .. + osgi.enroute.examples.eval $ vi pom.xml + // fill in from next section, right before the section +{: .shell } + + + api + simple.provider + + +We now have enough project information to build the bundle. As we've changed the parent pom, it's best to do a clean build from there. +And then we can take a look at how our module (bundle) really looks like. +Make sure you're in the top directory! + + osgi.enroute.examples.eval $ mvn clean install + ... + osgi.enroute.examples.eval $ cd simple.provider + simple.provider $ bnd print target/osgi.enroute.examples.eval.simple.provider-1.0.0-SNAPSHOT.jar + [MANIFEST osgi.enroute.examples.eval.provider-1.0.0-SNAPSHOT] + Bnd-LastModified 1474989660493 + Build-Jdk 1.8.0_25 + Built-By aqute + Bundle-Description Provides a simple implementation for an eval parser + Bundle-ManifestVersion 2 + Bundle-Name osgi.enroute.examples.eval.simple.provider + Bundle-SymbolicName osgi.enroute.examples.eval.simple.provider + Bundle-Version 1.0.0.201609271521 + Created-By 1.8.0_25 (Oracle Corporation) + Export-Package osgi.enroute.examples.eval.api;version="1.0.0" + Import-Package osgi.enroute.examples.eval.api;version="[1.0,1.1)" + Manifest-Version 1.0 + Private-Package osgi.enroute.examples.eval.provider + Provide-Capability osgi.service;objectClass:List="osgi.enroute.examples.eval.api.Eval" + Require-Capability osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))" + Require-Capability osgi.extender;filter:="(&(osgi.extender=osgi.component)(version>=1.3.0)(!(version>=2.0.0)))",osgi.service;filter:="(objectClass=org.osgi.service.log.LogService)";effective:=active,osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))" + Service-Component OSGI-INF/osgi.enroute.examples.eval.provider.xml + Tool Bnd-3.3.0.201609221906 + + [IMPEXP] + Import-Package + org.osgi.service.log {version=[1.3,2)} + osgi.enroute.examples.eval.api {version=[1.0,1.1)} + Export-Package + osgi.enroute.examples.eval.api {version=1.0.0, imported-as=[1.0,1.1)} +{: .shell } + +From this we can see the following layout of our provider bundle. + +* *Private packages* – Packages that are only available inside the bundle. Another + bundle could have the same name for the package but different contents. +* *Exported packages* – Packages that we provide to other bundles and which we are supposed to maintain over time. +* *Imported packages* – Packages we expect someone else to export, hoping that they are as good as we will be in maintaining that package over time. + +In a diagram: + +![Exporting the API](img/provider-layout.png) + + +## How Does it Work? + +The concepts of consumers and providers can be confusing, mostly because it is often +confused with implementers of an interface and the clients of an interface. However, +providers of a service API can both implement and/or be a client of interfaces in +the service package. A provider is responsible for providing the value of the contract, +and a consumer receives the value of the contract. The reason we need to distinguish +between these two roles is that they have far ranging consequences for how you package +and version bundles. + +Lets say you buy a house from me. In this scenario you are consumer of the contract +and I am the provider of the contract. These roles are, surprisingly, not symmetrical. +For example, if the seller adds an extra room after the contract was signed then the buyer +will not object (ok, in general, you get my point). However, if the seller removes +a room the buyer is going to be upset. A consumer can expect backward compatibility +but a provider is closely bound to the contract. Virtually any change in the service +contract will require a provider to be updated to provide the new functions. + +So a consumer is relatively distant from the contract and it often plays the role +of a consumer in many different service contracts. A provider usually provides only +a single service contact while being a consumer in other service contracts. + +Therefore the best practice in OSGi is for a provider to include its service API +codes and export it. Separating the API from the provider makes no sense since +there is a 1:1 relation between provider and API, unlike the consumer that will +get backward compatibility from the API. Having the API in the bundle just makes +life easier. That said, do not make the mistake to place them in the same _project_ +since that would require compiling against a JAR that would also contain the implementation. +Compilation should always be done against API only JARs to prevent accidentally +becoming dependent on implementation code. + +[Declarative Services]: /services/org.osgi.service.component.html + +## What Did We Learn? + +In this section we created a simplistic provider bundle for the Eval service API. +We exported the Eval service API in this bundle but also import it. + diff --git a/_tutorial_eval/360-provider-junit.md b/_tutorial_eval/360-provider-junit.md new file mode 100644 index 0000000..99b2716 --- /dev/null +++ b/_tutorial_eval/360-provider-junit.md @@ -0,0 +1,78 @@ +--- +title: Testing our Provider +layout: prev-next-collection +lprev: 350-provider +lnext: 380-run +summary: Create and run JUnit tests for our provider bundle +--- + +## What you will learn in this section + +In this section we will create a whitebox JUnit test for our simple.provider implementation. +JUnit tests are very cheap; using them extensively saves tremendous amount of times +in later phases of the development process. JUnit tests are always run before code +is released and when they fail, they prohibit the release of the project. + +These JUnit tests run outside the OSGi Framework. + +Testing is one of those chores a developer has to do, not as much fun as some deep +algorithmic code. However, it is likely one of the most effective ways to spend your time. + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + +## JUnit + +A provider should always have *unit* tests. Unit tests are *white* box tests. +The test knows about the implementation details and it can even see aspects of +the components that are not part of the public API. + +Since the JUnit tests run outside OSGi it pays off to design the implementation +in such a way that you have OSGi specific parts that then parameterize calls to +plain old java objects (POJO). These POJOs can then be tested independently + +In Maven, we have to write our test cases in `src/test/java`. + + osgi.enroute.examples.eval $ cd simple.provider + simple.provider $ mkdir -p src/test/java/osgi/enroute/examples/eval/simple/provider + simple.provider $ vi src/test/java/osgi/enroute/examples/eval/simple/provider/EvalImplTest.java + // copy the following source +{: .shell } + + package osgi.enroute.examples.eval.simple.provider; + + import junit.framework.TestCase; + import osgi.enroute.examples.eval.provider.EvalImpl; + + public class EvalImplTest extends TestCase { + public void testSimple() throws Exception { + EvalImpl t = new EvalImpl(); + assertEquals(3.0, t.eval("1 + 2")); + } + } + +We can run this test from maven: + + simple.provider $ mvn test + ... + ------------------------------------------------------- + T E S T S + ------------------------------------------------------- + Running com.acme.prime.eval.provider.EvalImplTest + Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.014 sec + + Results : + + Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 + ... +{: .shell } + + + +## What Did We Learn? + +In this section we learned how to write a plain JUnit test that tests the +implementation but does not require an OSGi runtime. + diff --git a/_tutorial_eval/380-run.md b/_tutorial_eval/380-run.md new file mode 100644 index 0000000..a4db6bc --- /dev/null +++ b/_tutorial_eval/380-run.md @@ -0,0 +1,418 @@ +--- +title: OSGi Runtime +layout: prev-next-collection +lprev: 360-provider-junit +lnext: 400-command +summary: How to create an OSGi Runtime +--- + +## What You Will Learn in This Section + +In this section we start by defining a _runtime_ environment. An OSGi runtime +environment consists of a classpath, a framework, and a number of configuration +properties that properly setup the framework. In contrast with the application +server model, the runtime is unique for the application. In this section +we show how to create an executable JAR that contains all the dependencies. + +Running a framework requires _assembling_ the application. Assembly consists of +selecting an appropriate set of bundles and defining the framework's context. + +This requires to select the _initial requirements_ of a runtime and then let bnd +resolve this into a set of _run bundles_. Once we have got this set of run bundles +we can export it into an executable JAR or other format. + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + + +## The POM + +In the `osgi.enroute.examples.eval` directory we create the `pom.xml` file in a new +`bndrun` directory. + + osgi.enroute.examples.eval $ mkdir bndrun + osgi.enroute.examples.eval $ cd bndrun + bndrun $ vi pom.xml + // add the following file +{: .shell } + + + 4.0.0 + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + + + osgi.enroute.examples.eval.bndrun + + jar + +We will define the OSGi runtime environment in a `bndrun` file. This file contains +the myriad of details that is needed to define an OSGi runtime. Details like the +class path, the set of bundles, system properties, framework configuration, etc. We +will use the `bnd-export-maven-plugin`. This plugin will take a bndrun file as input +and then generate an executable JAR. This is a JAR that contains all its dependencies. + + + + + +We're using the 3.4.0-SNAPSHOT revision because this tutorial needed a newer feature that missed 3.3 +{: .note } + biz.aQute.bnd + bnd-export-maven-plugin + 3.4.0-SNAPSHOT + +We need to configure the plugin. + + + true + + +Here we specify one or more bndrun files. We'll discuss the format of this file later. + + osgi.enroute.examples.eval.bndrun + + . + + + + + export + + + + + + + + +We need to add our provider as a runtime dependency: + + + + org.osgi + osgi.enroute.examples.eval.simple.provider + 1.0.0-SNAPSHOT + + +We also need a _distro_. So far we've been compiling against API only with the OSGi enRoute base +API. However, we now need to add implementations. Implementations can come from different sources. For +example IBM websphere, Karaf, Knopflerfish, Apache Felix, etc. However, the OSGi enRoute project +has created a distro that consists of many different open source projects to demonstrate +the interoperability of OSGi. + + + org.osgi + osgi.enroute.pom.distro + 2.0.0 + + + + + +We must also add bndrun as module in the parent pom.xml, e.g. after the existing api and simple.provider modules. + +## The bndrun file + +The `osgi.enroute.examples.eval.bndrun` bndrun file contains a specification of an +OSGi runtime. An OSGi runtime needs quite a bit of information and we collect that +all in this file. + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // add the following file +{: .shell } + +The `-standalone` instruction is there to indicate that this does not run in bndtools +but as a stand-alone definition. + + -standalone: + +We need access to all the dependencies of this project. The bndrun file supports this +with the Bnd Pom Repository. We then need to define the list of snapshot URLs and +release URLs. + + -plugin.examples.eval = \ + aQute.bnd.repository.maven.pom.provider.BndPomRepository; \ + snapshotUrls=https://oss.sonatype.org/content/repositories/osgi/; \ + releaseUrls=https://repo1.maven.org/maven2/; \ + pom=${.}/pom.xml; \ + name=examples.eval; \ + location=${.}/target/cached.xml + +In the `-runrequires` section we list OSGi requirements that we want our runtime +to have. In the initial case we only need the provider to run. The syntax for +the `-runrequires` is the syntax for an OSGi _requirement_, as used in the +OSGi Require-Capability header. Each clause consists of a _namespace_ and a +_filter_. The namespace defines a number of properties and the filter is an assertion +on these properties. In this case we use the identity namespace to require a specific +bundle, the provider bundle. The property name is `osgi.identity` following the +convention for namespaces and the Bundle Symbolic Name is `osgi.enroute.examples.eval.simple.provider`. + + -runrequires: \ + osgi.identity; \ + filter:='(osgi.identity=osgi.enroute.examples.eval.simple.provider)' + +The `-runfw` specifies the framework we want to use. + + -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' + +We can control the logging information during launching. Let's keep this off for now: + + -runtrace: false + +In the example we use Equinox and equinox has a number of habits that need to be unlearned. +For example, it has a built-in shell but it is better to define the used shell +using standard OSGi techniques. These settings will not be further explained here. + + # + # OSGi enRoute setting. These settings + # fixup some of the issues around equinox + # defaults. + # + + -runee: JavaSE-1.8 + -resolve.effective: resolve, active + + -runproperties.eqnx: \ + osgi.console.enable.builtin=false, \ + osgi.console=, \ + org.osgi.service.http.port=8080 + + -runpath.eqnx: osgi.enroute.equinox.log.adapter + -runrequires.eqnx: \ + osgi.identity;filter:='(osgi.identity=org.apache.felix.log)' + + -runsystempackages.eqnx: javax.script + -runsystemcapabilities.dflt: ${native_capability} + +## How to get the -runbundles + +The definition so far cannot be run. We need to _resolve_ the bndrun file. Resolving +will take the `-runrequires` and the repository and calculate a _closure_ of bundles. +This closure then is the list of `-runbundles` that we need in our runtime. + +We can resolve the requirements from Maven and get the list of bundles by running a `mvn install` +in this project. This will attempt to resolve the initial requirements and calculate the +list of bundles. If the list differs from the `-runbundles` setting then the install will +fail but it will give the list of calculated bundles: + + bndrun $ mvn install + ... + + -runbundles: \ + org.apache.felix.configadmin; version='[1.8.8,1.8.9)',\ + org.apache.felix.log; version='[1.0.1,1.0.2)',\ + org.apache.felix.scr; version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype; version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype; version='[1.3.0,1.3.1)',\ + osgi.enroute.examples.eval.simple.provider; version='[1.0.0,1.0.1)' + + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD FAILURE + ... + [INFO] ------------------------------------------------------------------------ +{: .shell } + +You can now copy this list and paste it into the `osgi.enroute.examples.eval.bndrun` +file at the end. Make sure there is only one (1) `-runbundles` instruction in the bndrun file. + +NOTE: You can add false inside bnd-export-maven-plugin configurations if you wish the update is done automatically. + +If we now run maven again we succeed and a JAR file is created in the project directory. + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // add/replace the -runbundles + bndrun $ mvn install + ... + bndrun $ ls + osgi.enroute.examples.eval.bndrun pom.xml target + osgi.enroute.examples.eval.jar src + {: .shell } + +## Running + +We can now run the application as follows: + + bndrun $ java -version + java version "1.8.0_25" + Java(TM) SE Runtime Environment (build 1.8.0_25-b17) + Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) + bndrun $ java -jar osgi.enroute.examples.eval.jar + + +The output is a little embarrassing ... + +Though rather disappointing, it does make sense since we've not created any bundle that +does something that is visual to us. So the lack of output is understandable. + +You can use Control-C to quit the application. + +## Tracing + +Since we do not see any output it would be nice to know if the launching was ok. +We can set the `-runtrace` instruction to `true` in the `osgi.enroute.examples.eval.bndrun` +file to see the launching progress: + + -runtrace: true + +If we build and run the application, we see the following: + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // set -runtrace: true + bndrun $ mvn install + ... + bndrun $ java -jar osgi.enroute.examples.eval.jar + # properties {launch.name=bndrun, osgi.console=, launch.noreferences=false, ... + ... + Id State Modified Location + 0 ACTIV <> System Bundle + 1 ACTIV <> jar/org.apache.felix.configadmin-1.8.8.jar + 2 ACTIV <> jar/org.apache.felix.log-1.0.1.jar + 3 ACTIV <> jar/org.apache.felix.scr-2.0.2.jar + 4 ACTIV <> jar/org.eclipse.equinox.metatype-1.4.100.v20150408-1437.jar + 5 ACTIV <> jar/org.osgi.service.metatype-1.3.0.jar + 6 ACTIV <> jar/osgi.enroute.examples.eval.provider-1.0.0-SNAPSHOT.jar + # framework=org.eclipse.osgi.launch.Equinox@7c0e2abd + # registered launcher with arguments for syncing + # will wait for a registered Runnable + +The output is quite extensive. If you have problems during the launch it is a great help +and including this information with a bug report is highly appreciated. + +## The Gogo Shell + +What we're missing is a shell to see the inner bowels of our runtime. The most +popular shell in OSGi is the Gogo shell. We can add this shell by adding it as +an initial run requirement in the `osgi.enroute.examples.eval.bndrun` file: + + -runrequires: \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.simple.provider)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.gogo.shell.provider)' + +You probably also want to set `-runtrace` to `false` to minimize the output. We +will now get a shell; as long as the shell works we can assume the launch was +ok. + +We now need to resolve the bndrun file. We run `mvn install` which will fail +because the new list of bundles does not match the current list of bundles. We +then copy the `-runbundles` into the `osgi.enroute.examples.eval.bndrun` file by +replacing the existing list. (Make sure you do not get more than 1 `-runbundles` +instruction in the file.) + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // add the -runrequires for Gogo + bndrun $ mvn install + .... + -runbundles: \ + org.apache.felix.configadmin; version='[1.8.8,1.8.9)',\ + org.apache.felix.gogo.runtime; version='[0.16.2,0.16.3)',\ + org.apache.felix.log; version='[1.0.1,1.0.2)',\ + org.apache.felix.scr; version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype; version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype; version='[1.3.0,1.3.1)',\ + osgi.enroute.examples.eval.provider; version='[1.0.0,1.0.1)',\ + osgi.enroute.gogo.shell.provider; version='[2.0.0,2.0.1)' + .... + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD FAILURE + [INFO] ------------------------------------------------------------------------ + bndrun $ vi osgi.enroute.examples.eval.bndrun + // copy the -runbundles from the output of mvn into the file + // replacing the existing -runbundles + bndrun $ mvn install + ... + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + bndrun $ java -jar osgi.enroute.examples.eval.jar + ____ _ + ___ _ __ | _ \ ___ _ _| |_ ___ + / _ \ '_ \| |_) / _ \| | | | __/ _ \ + | __/ | | | _ < (_) | |_| | |_ __/ + \___|_| |_|_| \_\___/ \__,_|\__\___| + https://v2archive.enroute.osgi.org + + G! +{: .shell } + +Gogo has many commands. You can find an introduction at the [Gogo App Note](/appnotes/gogo.html). + +For example: + + G! bundles + 0|Active | 0|org.eclipse.osgi (3.10.100.v20150529-1857) + 1|Active | 1|org.apache.felix.configadmin (1.8.8) + 2|Active | 1|org.apache.felix.gogo.runtime (0.16.2) + 3|Active | 1|org.apache.felix.log (1.0.1) + 4|Active | 1|org.apache.felix.scr (2.0.2) + 5|Active | 1|org.eclipse.equinox.metatype (1.4.100.v20150408-1437) + 6|Active | 1|org.osgi.service.metatype (1.3.0.201505202024) + 7|Active | 1|osgi.enroute.examples.eval.provider (1.0.0.201610030911) + 8|Active | 1|osgi.enroute.gogo.shell.provider (2.0.0.201608121439-SNAPSHOT) + G! +{: .shell } + +# User Friendly Commands + +What we're running is the base core primitive simple Gogo shell. This means it can run +the commands that are on the Bundle Context (that is `bundles` command is mapped +to `BundleContext.getBundles()`) but there are now user friendly commands. A number +of such commands are implemented in the `org.apache.felix.gogo.command` bundle. So we +could add this bundle to the `-runrequires` instruction: + + -runrequires: \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.simple.provider)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.gogo.shell.provider)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)' + +So now you can resolve again and then we can run `help` in the Gogo shell: + + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // replace the -runrequires + bndrun $ mvn install + ... + + -runbundles: ... + ... + bndrun $ vi osgi.enroute.examples.eval.bndrun + // replace -runbundles + bndrun $ java -jar osgi.enroute.examples.eval.jar + ____ _ + ___ _ __ | _ \ ___ _ _| |_ ___ + / _ \ '_ \| |_) / _ \| | | | __/ _ \ + | __/ | | | _ < (_) | |_| | |_ __/ + \___|_| |_|_| \_\___/ \__,_|\__\___| + https://v2archive.enroute.osgi.org + + + G! help + felix:bundlelevel + felix:cd + felix:frameworklevel + felix:headers + ... + scr:config + scr:disable + scr:enable + scr:info + scr:list + G! +{: .shell } + +## What did We Learn? + +In this section we've created a bndrun file to create an OSGi runtime. We then +learned how to add requirements to the file and resolve it so we get the `-runbundles`. +We then learned how to add these `-runbundles` to the bndrun file so they can +be exported into an executable jar. + +We then executed this JAR and added a Gogo shell. + + diff --git a/_tutorial_eval/400-command.md b/_tutorial_eval/400-command.md new file mode 100644 index 0000000..f5e1457 --- /dev/null +++ b/_tutorial_eval/400-command.md @@ -0,0 +1,241 @@ +--- +title: Command Project +layout: prev-next-collection +lprev: 380-run +lnext: 430-dependencies +summary: Create a shell command that exercises the evaluation parser +--- + +## What you will learn in this section + +So far, the provider is idly sitting in the framework registering an Eval service. +In this section we develop a simple Gogo command that allows us to call this +service and test it from the command line. + +A Gogo command is a simple service that has some magic properties. It can +implement multiple commands. + +![API Diagram](img/command-diagram.png) + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + + +## Creating a POM + +We need to create a directory called `command` in the `osgi.enroute.examples.eval` +directory. In this directory we need to create a `pom.xml` file. The POM is quite standard now: + + + osgi.enroute.examples.eval $ mkdir command + osgi.enroute.examples.eval $ cd command + command $ vi pom.xml + // Add the pom.xml +{: .shell } + +And the content: + + + 4.0.0 + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + + jar + + osgi.enroute.examples.eval.command + + + + org.osgi + osgi.enroute.examples.eval.api + 1.0.0-SNAPSHOT + + + + +We must also add command as module in the parent pom.xml, as we've done for the modules of the previous steps in this tutorial. + +## The Command + +The Gogo shell is quite clever. It listens to services that have 2 'magic' properties. +The `osgi.command.scope` service property defines a _scope_ for the command. The +`osgi.command.function` contains a list of commands. These are mapped to methods +on the service object. The service property keys are defined in the `Debug` class +in the OSGi enRoute API. + +Our implementation joins any given strings in the command line and calls the evaluator Eval service. +In Gogo, the command can just return the value as is. Gogo will automatically format that +in the best possible way. That is, don't write commands in the `main` style that +return a String. The idea is that the command methods are normal methods, not +specially designed for shells. Gogo for example, tries to use the bean naming +strategy to map a name like `foo` to `getFoo`, `setFoo`, `isFoo`, etc. + +The command component we're designing is dependent on the `Eval` service. We +therefore use DS to add a `@Reference` to an instance field. This will get us +injected with the Eval service once it is available. + +The following code implements a command with a scope of `eval` and a function +of `eval`. + + + command $ mkdir -p src/main/java/osgi/enroute/examples/eval/command + command $ vi src/main/java/osgi/enroute/examples/eval/command/EvalCommand.java + // add the Java code +{: .shell } + +And the content: + + package osgi.enroute.examples.eval.command; + + import java.util.stream.Collectors; + import java.util.stream.Stream; + + import org.osgi.service.component.annotations.Component; + import org.osgi.service.component.annotations.Reference; + + import osgi.enroute.debug.api.Debug; + import osgi.enroute.examples.eval.api.Eval; + + @Component(property= { + Debug.COMMAND_SCOPE+"=eval", + Debug.COMMAND_FUNCTION+"=eval" }, + service=EvalCommand.class) + public class EvalCommand { + + @Reference + Eval greeter; + + public double eval(String ... name) throws Exception { + return greeter.eval(Stream.of(name).collect(Collectors.joining(" "))); + } + } + +## The bnd.bnd File + +There is no need for a `bnd.bnd` file because by default, all packages in the +project are placed in the bundle. Which is exactly what we want! + +## Building + +We now install our bundle: + + command $ mvn install + ... +{: .shell } + +## Adding the Command Bundle to the Runtime + +In the `bndrun` project we must now add the command project to the pom so that +it becomes a dependency. We should also add the Gogo shell, since it isn't +included in the enRoute distro. + + command $ cd ../bndrun + bndrun $ vi pom.xml + // update the dependency section to add our command +{: .shell } + +The dependency section in the bndrun pom.xml file should +therefore look like the following. + + + + org.osgi + osgi.enroute.examples.eval.simple.provider + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.pom.distro + 2.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.examples.eval.command + 1.0.0-SNAPSHOT + + + org.apache.felix + org.apache.felix.gogo.shell + 1.0.0 + + + +We also need to add the bundle to our initial requirements in the bndrun file +in the `osgi.enroute.examples.eval/bndrun` directory: + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // replace the -runrequires +{: .shell } + + -runrequires: \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.simple.provider)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.command)' + +And then we run the command to resolve: + + bndrun $ mvn install + ... + + -runbundles: \ + org.apache.felix.configadmin; version='[1.8.8,1.8.9)',\ + org.apache.felix.gogo.command; version='[0.16.0,0.16.1)',\ + org.apache.felix.gogo.runtime; version='[1.0.0,1.0.1)',\ + org.apache.felix.gogo.runtime; version='[0.16.2,0.16.3)',\ + org.apache.felix.gogo.shell; version='[1.0.0,1.0.1)',\ + org.apache.felix.log; version='[1.0.1,1.0.2)',\ + org.apache.felix.scr; version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype; version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype; version='[1.3.0,1.3.1)',\ + osgi.enroute.examples.eval.command; version='[1.0.0,1.0.1)',\ + osgi.enroute.examples.eval.parsii.provider; version='[1.0.0,1.0.1)' + + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD FAILURE + ... + [INFO] ------------------------------------------------------------------------ + bndrun $ vi osgi.enroute.examples.eval.bndrun + // replace the -runbundles with the given list +{: .shell } + +And then replace the `-runbundles` in the `osgi.enroute.examples.eval.bndrun` +file to the list provided by maven. Then we should run maven again to get our +JAR. + + bndrun $ mvn install + ... + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + ... + [INFO] ------------------------------------------------------------------------ + bndrun $ java -jar osgi.enroute.examples.eval.jar + ____ _ + ___ _ __ | _ \ ___ _ _| |_ ___ + / _ \ '_ \| |_) / _ \| | | | __/ _ \ + | __/ | | | _ < (_) | |_| | |_ __/ + \___|_| |_|_| \_\___/ \__,_|\__\___| + https://v2archive.enroute.osgi.org + + + G! eval 3 + 4 + 7.0 + G! +{: .shell } + +Yeah! + +## What Have We Learned? + +In this section we learned how to create a Gogo command. We created a bundle +that registered a service that was picked up by the Gogo shell. We then used this +command to test our bundle. diff --git a/_tutorial_eval/430-dependencies.md b/_tutorial_eval/430-dependencies.md new file mode 100644 index 0000000..779acc1 --- /dev/null +++ b/_tutorial_eval/430-dependencies.md @@ -0,0 +1,266 @@ +--- +title: Dependencies +layout: prev-next-collection +lprev: 400-command +lnext: 450-web +summary: Use a standard parser instead of our simplistic parser +--- + +## What You Will Learn in this Section + +In this section we will replace the rather simplistic parser with an external +dependency from Maven Central. This dependency is [Parsii], a very lightweight expression +parser. + +![API Diagram](img/parsii-diagram.png) + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + + +## Create Project + +Create a new project for the osgi.enroute.examples.parsii.provider in the `parsii.provider` +directory of the `osgi.enroute.examples.eval` directory. The `pom.xml` file should look like: + + osgi.enroute.examples.eval $ mkdir parsii.provider + osgi.enroute.examples.eval $ cd parsii.provider + parsii.provider $ vi pom.xml + // add following pom +{: .shell } + + + 4.0.0 + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + + + osgi.enroute.examples.eval.parsii.provider + Eval Provider based on Parsii library + + + + com.scireum + parsii + 2.3 + + + org.osgi + osgi.enroute.examples.eval.api + 1.0.0-SNAPSHOT + + + + +This is quite similar to the simple provider that we had earlier but now with +an extra dependency for the Parsii parser. + +## Source Code + +We can use the following source code: + + parsii.provider $ mkdir -p src/main/java/osgi/enroute/examples/eval/parsii/provider + parsii.provider $ vi src/main/java/osgi/enroute/examples/eval/parsii/provider/EvalImpl.java + // Add the following code +{: .shell } + + package osgi.enroute.examples.eval.parsii.provider; + + import org.osgi.service.component.annotations.Component; + import org.osgi.service.component.annotations.Reference; + import org.osgi.service.log.LogService; + + import osgi.enroute.examples.eval.api.Eval; + import parsii.eval.Parser; + + @Component(name = "osgi.enroute.examples.eval.parsii.provider") + public class EvalImpl implements Eval { + + @Reference + LogService log; + + @Override + public double eval(String expression) throws Exception { + return Parser.parse(expression).evaluate(); + } + } + +The reference to the log service is not used. This line was added to solve +a bug in bnd that should soon be solved. The problem was that for a very simple +DS use no `osgi.extender` requirement is included. +{: .note } + +## Bundle Layout + +The bnd.bnd file is a bit more complex than the simple provider we made before. The +reason is that the dependency we added is not a bundle, we can therefore not +depend on this JAR in the runtime. In these cases, there are two different tracks +to take. You can either _wrap_ this bundle or _include_ it. Wrapping a bundle means +creating a new project and providing the OSGi metadata. Including the JAR means +including the required packages from the JAR with bnd's Private-Package instruction. + +In this example we've selected the including strategy. The bnd.bnd file therefore looks as +follows: + + parsii.provider $ vi bnd.bnd + // add the following bnd.bnd +{: .shell } + + Export-Package: osgi.enroute.examples.eval.api + + Private-Package: \ + parsii.*, \ + osgi.enroute.examples.eval.parsii.provider + +Just like the simple provider, we've exported the API. However, where we've used +the default of all classes from the project for the simple provider, here we +explicitly list the packages that we want included. + +Maven does not clean the classes directory for a build automatically. This means +that only after a `mvn clean` can you be assured of the right content. In practice, +it is easy to end up with classes from long gone experiments with the bnd.bnd file. +{: .note } + +## Building + +Again, don't forget to add the parsii.provider module to the parent pom. + +We now build the bundle: + + parsii.provider $ mvn install + ... + parsii.provider $ bnd print target/osgi.enroute.examples.eval.parsii.provider-1.0.0-SNAPSHOT.jar + [MANIFEST osgi.enroute.examples.eval.parsii.provider-1.0.0-SNAPSHOT] + Bnd-LastModified 1475601027531 + Build-Jdk 1.8.0_25 + Built-By aqute + Bundle-ManifestVersion 2 + Bundle-Name osgi.enroute.examples.eval.parsii.provider + Bundle-SymbolicName osgi.enroute.examples.eval.parsii.provider + Bundle-Version 1.0.0.201610041710 + Created-By 1.8.0_25 (Oracle Corporation) + Export-Package osgi.enroute.examples.eval.api;version="1.0.0" + Import-Package osgi.enroute.examples.eval.api;version="[1.0,1.1)" + Manifest-Version 1.0 + Private-Package parsii.tokenizer,parsii.eval,osgi.enroute.examples.eval.parsii.provider + Provide-Capability osgi.service;objectClass:List="osgi.enroute.examples.eval.api.Eval" + Require-Capability osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))" + Service-Component OSGI-INF/osgi.enroute.examples.eval.parsii.provider.xml + Tool Bnd-3.3.0.201609221906 + + [IMPEXP] + Import-Package + osgi.enroute.examples.eval.api {version=[1.0,1.1)} + Export-Package + osgi.enroute.examples.eval.api {version=1.0.0, imported-as=[1.0,1.1)} +{: .shell } + +The Private-Package header added by bnd clearly shows that we've added the `parsii` packages that it found in the bundle. We specified a wild card in the `bnd.bnd` file for Private-Package but in the manifest we can clearly see that there are `parsii.tokenizer` and `parsii.eval` packages on the classpath. + +## Running It + +We should now switch to the bndrun project. + + parsii.provider $ cd ../bndrun + parsii.provider $ vi pom.xml + // replace dependencies with next section +{: .shell } + +We first need to replace the simple provider with our newly eval parser in the pom.xml in this project so +it can be used by the resolver. The dependencies should therefore look like: + + + + org.osgi + osgi.enroute.examples.eval.parsii.provider + 1.0.0-SNAPSHOT + + + org.apache.felix + org.apache.felix.gogo.shell + 1.0.0 + + + org.osgi + osgi.enroute.examples.eval.command + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.pom.distro + 2.0.0 + + + +In the bndrun project in the `osgi.enroute.examples.eval.bndrun` file, we need to +change the `-runrequires` to use the `parsii.provider` instead of the simple provider: + + + parsii.provider $ cd ../bndrun + bndrun $ vi osgi.enroute.examples.eval.bndrun + // Replace the -runrequires +{: .shell } + + -runrequires: \ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.command)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.parsii.provider)' + +Then resolve, which should give something like: + + bndrun $ mvn install + ... + -runbundles: \ + org.apache.felix.configadmin; version='[1.8.8,1.8.9)',\ + org.apache.felix.gogo.command; version='[0.16.0,0.16.1)',\ + org.apache.felix.gogo.runtime; version='[0.16.2,0.16.3)',\ + org.apache.felix.log; version='[1.0.1,1.0.2)',\ + org.apache.felix.scr; version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype; version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype; version='[1.3.0,1.3.1)',\ + osgi.enroute.examples.eval.command; version='[1.0.0,1.0.1)',\ + osgi.enroute.examples.eval.parsii.provider; version='[1.0.0,1.0.1)',\ + org.apache.felix.gogo.shell; version='[2.0.0,2.0.1)' + ... +{: .shell } + +And then update the `osgi.enroute.examples.eval.bndrun` file with the returned `-runbundles` and run `mvn install` again. + +We can then run the application: + + bndrun $ java -jar osgi.enroute.examples.eval.jar + ____ _ + ___ _ __ | _ \ ___ _ _| |_ ___ + / _ \ '_ \| |_) / _ \| | | | __/ _ \ + | __/ | | | _ < (_) | |_| | |_ __/ + \___|_| |_|_| \_\___/ \__,_|\__\___| + https://v2archive.enroute.osgi.org + G! eval sin(pi) + 1.2246467991473532E-16 + G! eval cos(pi) + -1.0 + G! +{: .shell } + +## What did We Learn in this Section? + +We created a new provider that used an external dependency. We needed to include +the code from this dependency inside our new bundle because the Parsii JAR does +not contain any OSGi metadata. + +We then added this new project to the modules and updated the bndrun project +to use the new Parsii provider. + +And it worked! + +[Parsii]: https://github.com/scireum/parsii diff --git a/_tutorial_eval/450-web.md b/_tutorial_eval/450-web.md new file mode 100644 index 0000000..4cc2ce7 --- /dev/null +++ b/_tutorial_eval/450-web.md @@ -0,0 +1,616 @@ +--- +title: A Web Application +layout: prev-next-collection +lprev: 430-dependencies +lnext: 480-testing +summary: Create a one page web app that uses our evaluator +--- + +## What You Will Learn in this Section + +So far we've been able to test our expression evaluator with the Gogo command shell. In +this section we create a small web application that calls the evaluator from the +web. + +We will create a small GUI in HTML 5 and Javascript based on Angular that uses +the OSGi enRoute simple REST facility to call the evaluator service. + +For many Java developers, Javascript, CSS, and HTML 5 are foreign and scary. However, this example requires very little knowledge of these resources. And won't you admit that it's cool to write something you can show your girl/boy friend without getting blank stares? If you're absolutely not interested in web applications then you can skip this section. + +![API Diagram](img/application-diagram.png) + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + + +## Project + + osgi.enroute.examples.eval $ mkdir application + osgi.enroute.examples.eval $ cd application + application $ vi pom.xml + // edit pom +{: .shell } + +The pom for this project looks like: + + + 4.0.0 + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + + + osgi.enroute.examples.eval.application + A web application to evaluate expressions + + + + org.osgi + osgi.enroute.examples.eval.api + 1.0.0-SNAPSHOT + compile + + + + +## The Application Java Source Code + +Our application class must provide access to the expression evaluator via REST. +For this reason, there is a simple REST provider that leverages the OSGi model. The +primary purpose of this support is to make it trivially easy to call Java code +running in OSGi as a service from a Javascript application. + +The secondary purpose of the application class is to act as a _root_ for the +requirements graph. It defines a number of requirements that cannot be analyzed +from the code. We add those requirements using annotations. These annotations +will then be converted to OSGi requirements by bnd. + + application $ mkdir -p src/main/java/osgi/enroute/examples/eval/application + application $ vi src/main/java/osgi/enroute/examples/eval/application/EvalApplication.java + // Add the source code +{: .shell } + +The source code is: + + package osgi.enroute.examples.eval.application; + + import org.osgi.service.component.annotations.Component; + import org.osgi.service.component.annotations.Reference; + + import osgi.enroute.configurer.api.RequireConfigurerExtender; + import osgi.enroute.examples.eval.api.Eval; + import osgi.enroute.google.angular.capabilities.RequireAngularWebResource; + import osgi.enroute.rest.api.REST; + import osgi.enroute.twitter.bootstrap.capabilities.RequireBootstrapWebResource; + import osgi.enroute.webserver.capabilities.RequireWebServerExtender; + + @RequireAngularWebResource(resource={"angular.js","angular-resource.js", "angular-route.js"}, priority=1000) + @RequireBootstrapWebResource(resource="css/bootstrap.css") + @RequireWebServerExtender + @Component(name="osgi.enroute.examples.eval") + public class EvalApplication implements REST { + + @Reference + Eval eval; + + public double getEval(String string) throws Exception { + return eval.eval(string); + } + } + +A short explanation of the annotations: + +* `@RequireAngularWebResource` – Instead of listing Javascript and CSS dependencies + in the `index.html` file we can define what gets included with the annotation. This + provides for a single definition of what resources should be included. This + Angular annotation includes the angular Javascript resources. +* `@RequireBootstrapWebResource` – Includes the Bootstrap CSS +* `@RequireWebServerExtender` – The [Webserver] extender makes it easy to provide static + context. It also provides the runtime support for the web resources. + +## The GUI + +We want the application to look like: + +![The application](img/app-0.png) + +### About Angular + +For many Java developers the web is a scary place. In OSGi enRoute we chose Angular +for the GUI not expecting every Java OSGi developer to jump on that band wagon (this +band wagon changes every 3 months anyway) but it allows very simple powerful and scalable +applications. + +The key concept of Angular is that it updates the GUI directly from the Javascript +variables. This allows very clean separation between the business logic, the traditional model, +and the GUI. The GUI itself is modeled with HTML 5 and also often contains logic. However, +this view/control logic should be restricted to the control and view aspects and not contain +business logic. + +### About HTML 5 + +Most Java developers still consider HTML to be a variant of XML. It is not, it is much +more relaxed. In many cases it is not necessary to quote attribute values and +many tags are not required to be closed. Though this sounds offensive to people +used to XML it is actually more readable and sometimes not closing a tag +prevents accidental whitespace between tags to kill a carefully crafted layout. + +## Static Web Content + +The OSGi enRoute webserver bundle will map any directory named `static` in a bundle to the +web server's root. In OSGi enRoute applications, the convention is to use the fully +qualified name of the application as the root. This content is generally not accessed +directly except for the `index.html` file in this directory. We therefore have the following +static web resource files: + + static/ + osgi.enroute.examples.eval/ + index.html + favicon.ico + htm/ + main/ + about.htm + home.htm + +### index.html + +The `index.html` file is the root of our Javascript application. It is an Angular +application. It uses URL based routing, this makes it slightly more complex than +the absolute simplest Angular application but it then makes turning it into something +useful much harder. + +In the `index.html` file we are using [_macros_][Macro] and/or properties. We therefore need to +let bnd _expand_ the file. This is needed because the web resources use the Bundle +Symbolic Name and the actual version used in the bundle, which might differ slightly from +the Maven version. + +The `index.html` should be stored in `src/main/resources/static/osgi.enroute.examples.eval/index.html`, this +will make it available on the web server under `/osgi.enroute.examples.eval/index.html`. + + application $ mkdir -p src/main/resources/static/osgi.enroute.examples.eval + application $ vi src/main/resources/static/osgi.enroute.examples.eval/index.html + // add the subsequent content +{: .shell } + +The first part of the index.html file is quite straightforward HTML 5 code made compatible with +the Internet Explorer quirks. + + + + + + + + + + + + + + OSGi ENROUTE EXAMPLE APPLICATION + +The following link will include the CSS that the application needs. The CSS (in our +case Bootstrap) was indicated by the @RequireBootstrap annotation on the application +class. The `/osgi.enroute.webresource/` URL is mapped to a servlet that will be able +to find all the required web resources for the given Bundle-SymbolicName and Bundle Version. +The ${bsn} macro and ${Bundle-Version} macro are expanded by bnd. + + + + + +Angular requires an `ng-app` attribute for the name of the application. + + + +We use Bootstrap to create a container for the content. + +
+ +
+ +The first part is a navigation bar where we have our 2 views listed. The Home view is our application, +executing an expression evaluation. The second one is an About window. We use the URL +routing mechanism to go to the right view. + + + +

+ OSGi enRoute Example Application +

+ +A simple alert area. This allows us to report errors like communication errors. We add +a close button to remove the alerts one by one. + +
+ {% endraw %} + + {{alert.msg}} + + +
+ +
+ +This is the Angular magic. This element will be replaced by the content (`htm/main/home.htm` or `htm/main/about.htm`) +depending on the URL. + +
+
+ +A simple footer. If you set the Bundle-Vendor header in the bnd.bnd file then it will be used here. Otherwise +it will use the generic `Company` title. Also note how we can use the build time stamp. You can use +any bnd [Macro]. + + + +
+ +This is where the magic happens for Javascript web resources. We include any resource that ends +with .js that has been required by the bundle. At the end, also the content of the `web` directory +in the bundle is appended. + + + + + + + +To replace the bnd macros with their value we must tell bnd to pre-process the resource. We therefore +need to create a bnd.bnd file: + + application $ vi bnd.bnd + // Add content +{: .shell } + +This requires the following line in the `bnd.bnd` file: + + -includeresource: \ + {static/osgi.enroute.examples.eval/index.html=src/main/resources/static/osgi.enroute.examples.eval/index.html} + +## The Views + +The app has two views. They are stored in `src/main/resources/static/osgi.enroute.examples.eval/main/htm`. + + application $ mkdir -p src/main/resources/static/osgi.enroute.examples.eval/main/htm +{: .shell } + +## Home + +The `home.htm` is the main view. + + application $ vi src/main/resources/static/osgi.enroute.examples.eval/htm/main/home.htm + // add the content +{: .shell } + +It looks like: + +
+ +

Enter an arithmetic expression like sin(pi) + +

+ +The input field for the expression. + + + + + +
+ +
+ +
+ +Prints all the answers + +
+ {% raw %} + {{i}} + {% endraw %} +
+ +
+
+ + +## About + +A simple about box. + + application $ vi src/main/resources/static/osgi.enroute.examples.eval/htm/main/about.htm + // add the content +{: .shell } + +
+

About

+

This is a demo application to demonstrate how to build OSGi enRoute Applications with + Maven/M2E +

+

+ +## The Javascript Code + +The Javascript code must be placed in a directory in the `src/resources/main/web`. Any +content in this directory is treated as a web resource and automatically included +in the `index.html` page. The name of the files is irrelevant but the extension is +since the selection in the index.html file takes place on extension. + + application $ mkdir -p src/main/resources/web +{: .shell } + +### main.js + + application $ vi src/main/resources/web/main.js + // add the content +{: .shell } + +We create a Javascript 'module'. This is an anonymous function. + + 'use strict'; + (function() { + +We now create an Angular module. The name of the module must match the `ng-app` +attribute in the `index.html` file. + + var MODULE = + angular.module('osgi.enroute.examples.eval', [ 'ngRoute' ] ); + +The next section configures the routing table. The routing table matches a pattern +in the URL and then selects the content of the `ng-view` element in the `index.html` +page. + + MODULE.config( function($routeProvider) { + $routeProvider.when('/', { controller: mainProvider, templateUrl: '/osgi.enroute.examples.eval/htm/main/home.htm'}); + $routeProvider.when('/about', { templateUrl: '/osgi.enroute.examples.eval/htm/main/about.htm'}); + $routeProvider.otherwise('/'); + }); + +Initialize when the module is ready to run. We add the data structure to hold the alerts and provide +access to the current page name for the nav bar. + + MODULE.run( function($rootScope, $location) { + $rootScope.alerts = []; + $rootScope.closeAlert = function(index) { + $rootScope.alerts.splice(index, 1); + }; + $rootScope.page = function() { + return $location.path(); + } + }); + +A list of answers, i.e. the history. + + var answers = [ ]; + +This _controller_ is called when the URL ends in `#home`. We set the history and add +an `eval` method. This method will call the server. If we receive an answer, we +add it to the history. Angular will then make sure it gets printed. + + var mainProvider = function($scope, $http) { + $scope.answers=answers; + + $scope.eval = function(expr) { + $http.get('/rest/eval/'+expr).then( + function(d) { + answers.push(d.data + " = " + expr); + }, function(d) { + $scope.alerts.push( { type: 'danger', msg: 'Failed with ['+ d.status + '] '+ d.statusText }); + } + ); + } + } + + })(); + +## Building + +You should add the application module to the parent pom and then build the bundle: + + application $ vi ../pom.xml + // add application module + application $ mvn install + ... +{: .shell } + +## Running + +To run this application we need to add this bundle to the bndrun file. This requires +the following steps in the bndrun project. + + application $ cd ../bndrun + bndrun $ vi pom.xml + // edit the dependencies section +{: .shell } + +The dependencies section should look like: + + + + org.osgi + osgi.enroute.examples.eval.parsii.provider + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.examples.eval.command + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.examples.eval.application + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.pom.distro + 2.0.0 + + + org.apache.felix + org.apache.felix.gogo.shell + 1.0.0 + + + +Then add the new `-runrequires` for the application: + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // Update -runrequires +{: .shell } + + -runrequires: \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.parsii.provider)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\ + osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.command)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.application)' + +Then run `mvn install` to get the new list of `-runbundles`. This will give us a much larger +list because we now get all the web support: + + + bndrun $ mvn install + ... + -runbundles: \ + org.apache.felix.configadmin;version='[1.8.8,1.8.9)',\ + org.apache.felix.gogo.command;version='[0.16.0,0.16.1)',\ + org.apache.felix.gogo.runtime;version='[0.16.2,0.16.3)',\ + org.apache.felix.log;version='[1.0.1,1.0.2)',\ + org.apache.felix.scr;version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype;version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype;version='[1.3.0,1.3.1)',\ + osgi.enroute.examples.eval.command;version='[1.0.0,1.0.1)',\ + osgi.enroute.examples.eval.parsii.provider;version='[1.0.0,1.0.1)',\ + org.apache.felix.gogo.shell;version='[2.0.0,2.0.1)',\ + org.apache.felix.http.jetty;version='[3.2.0,3.2.1)',\ + org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ + org.eclipse.equinox.coordinator;version='[1.3.100,1.3.101)',\ + org.eclipse.equinox.event;version='[1.3.100,1.3.101)',\ + org.osgi.service.event;version='[1.3.1,1.3.2)',\ + osgi.enroute.configurer.simple.provider;version='[2.0.0,2.0.1)',\ + osgi.enroute.dtos.bndlib.provider;version='[2.0.0,2.0.1)',\ + osgi.enroute.examples.eval.application;version='[1.0.0,1.0.1)',\ + osgi.enroute.executor.simple.provider;version='[1.0.0,1.0.1)',\ + osgi.enroute.google.angular.webresource;version='[1.5.7,1.5.8)',\ + osgi.enroute.logger.simple.provider;version='[2.0.0,2.0.1)',\ + osgi.enroute.rest.simple.provider;version='[2.0.0,2.0.1)',\ + osgi.enroute.twitter.bootstrap.webresource;version='[3.3.5,3.3.6)',\ + osgi.enroute.web.simple.provider;version='[2.0.0,2.0.1)' + ... +{: .shell } + +Add the new `-runbundles` to the `bndrun` file and run `mvn install` again. + + bndrun $ vi osgi.enroute.examples.eval.bndrun + // Update the -runbundles + bndrun $ mvn install + ... + bndrun $ java -jar osgi.enroute.examples.eval.jar + 2016-10-04 11:44:50.960:INFO::main: Logging initialized @673ms + 2016-10-04 11:44:50.989:WARN:oejs.session:main: Sessions created by this manager are immortal (default maxInactiveInterval={})0 + 2016-10-04 11:44:50.992:INFO:oejs.Server:main: jetty-9.3.8.v20160314 + 2016-10-04 11:44:51.029:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@1757cd72{/,null,AVAILABLE} + 2016-10-04 11:44:51.030:INFO:oejs.Server:main: Started @742ms + 2016-10-04 11:44:51.049:INFO:oejs.ServerConnector:main: Started ServerConnector@68c72235{HTTP/1.1,[http/1.1]}{0.0.0.0:8080} + ____ _ + ___ _ __ | _ \ ___ _ _| |_ ___ + / _ \ '_ \| |_) / _ \| | | | __/ _ \ + | __/ | | | _ < (_) | |_| | |_ __/ + \___|_| |_|_| \_\___/ \__,_|\__\___| + https://v2archive.enroute.osgi.org + + + G! +{: .shell } + +The output from Gogo and the log can be garbled. + +We can now open a browser on [http://localhost:8080/osgi.enroute.examples.eval](http://localhost:8080/osgi.enroute.examples.eval) + +What we get is the home page of our application, with a text entry field to try out the expression evaluator! + + +## Index + +If you go to the root of the webserver you get an empty page. It is possible to get the active web application(s) listed here. +For this, we need to add some metadata to the application project. + + bndrun $ cd ../application + application $ vi bnd.bnd + // Add the following content +{: .shell } + + Bundle-Description: \ + Provides a Web based application to evaluate the evaluator + + EnRoute-Application: osgi.enroute.examples.eval + + -includeresource: \ + {static/osgi.enroute.examples.eval/index.html=\ + src/main/resources/static/osgi.enroute.examples.eval/index.html} + +If we now build and then run again then we should see the application +listed on the home page. + + application $ mvn install + ... + application $ cd ../bndrun + bndrun $ mvn install + bndrun $ java -jar osgi.enroute.examples.eval.jar + ... + + +This list is created by the web server if nothing else +is registered at the root. This list looks like: + +![OSGi enRoute Index](img/index.png) + +Click on the link and you should get the application in the browser. + +We can now open a browser on [http://localhost:8080](http://localhost:8080) to try this out. + +[Webserver]: /services/osgi.enroute.webserver.capabilities.html +[Configurer]: /services/osgi.enroute.configurer.api.html +[Macro]: http://bnd.bndtools.org/chapters/850-macros.html diff --git a/_tutorial_eval/480-testing.md b/_tutorial_eval/480-testing.md new file mode 100644 index 0000000..c17d271 --- /dev/null +++ b/_tutorial_eval/480-testing.md @@ -0,0 +1,302 @@ +--- +title: Integration Testing +layout: prev-next-collection +lprev: 450-web +lnext: 490-modules +summary: Run integration tests +--- + +## What you will learn in this section + +In this section we will create an OSGi integration test. We will use +the bnd-testing-maven-plugin to setup a runtime environment and then +run our JUnit tests inside OSGi. + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + + +## Create a Test Bundle + +We will first create a test bundle. A test bundle is one one or more classes +with JUnit tests. Each bundle will list its test classes in the Test-Cases +manifest header. The bnd runtime support has a tester that will look for bundles +with this header and then automatically runs these test. + +In this instance, we want to define a test to verify the Eval service. + +## Create the pom + +Let's create the pom. + + osgi.enroute.examples.eval $ mkdir test + osgi.enroute.examples.eval $ cd test + test $ vi pom.xml + // get the content +{: .shell } + + + + 4.0.0 + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + + + osgi.enroute.examples.eval.test + +We need the API bundle and JUnit as dependencies. We use the OSGi enRoute wrapped +version of JUnit because JUnit normally does not contain OSGi metadata and many +wrappers have problems. + + + + org.osgi + osgi.enroute.examples.eval.api + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.junit.wrapper + 4.12.0 + + + + + +## The Source Code + +It is a tad confusing but the source code for an integration test bundle *must* be +in the `src/main/java` directory, *not* in the `test` directory. This makes sense +if you realize that we're making a bundle and the sole purpose of a test branch +in the src directory is to make sure that code does not end up in a bundle. + + test $ mkdir -p src/main/java/osgi/enroute/examples/eval/test + test $ vi src/main/java/osgi/enroute/examples/eval/test/EvalTest.java + // get the content from the next section +{: .shell } + + package osgi.enroute.examples.eval.test; + + import org.osgi.framework.BundleContext; + import org.osgi.framework.FrameworkUtil; + import org.osgi.framework.ServiceReference; + + import junit.framework.TestCase; + import osgi.enroute.examples.eval.api.Eval; + + public class EvalTest extends TestCase { + BundleContext context = FrameworkUtil.getBundle(EvalTest.class).getBundleContext(); + + public void testEval() throws Exception { + ServiceReference ref = context.getServiceReference(Eval.class); + assertNotNull("No such service", ref); + Eval eval = context.getService(ref); + assertNotNull("Service object init error", eval); + assertEquals( 7.0D, eval.eval("1+6")); + } + } + +Remember that the bnd runtime support for testing expects the Test-Cases manifest +header to be set. We therefore need a bnd.bnd file. + + test $ vi bnd.bnd + // get the content from the next section +{: .shell } + + Bundle-Description: \ + Integration Test bundle for the Eval service + + Test-Cases: ${classes;NAMED;*Test} + +The magic in this case is the macro `classes`. It will list all classes that +end with `Test`. + +## Install + +That's all, we created a test bundle. So we need to install it. + + test $ mvn install + ... +{: .shell } + +You should also add this project to our parent pom. + +## The Integration Test Project + +Integration testing requires a runtime environment. We have been using the +bndrun file to create a runtime environment for our application. We can use a +similar bndrun file, or more than one, to perform our integration tests. + +Multiple environments can be useful if you want to test on different platforms. + +## Create the Integration Project and Pom + + test $ cd .. + osgi.enroute.examples.eval $ mkdir integration-test + osgi.enroute.examples.eval $ cd integration-test + integration-test $ vi pom.xml + // get content from next section +{: .shell } + +The content of our pom is: + + + 4.0.0 + + + org.osgi + osgi.enroute.examples.eval + 1.0.0-SNAPSHOT + .. + + + osgi.enroute.examples.eval.integration-test + + pom + + + + +This plugin will iterate over a set of bndrun files and execute any tests in them. + + + biz.aQute.bnd + bnd-testing-maven-plugin + 3.4.0-SNAPSHOT + + true + + +The relative file path of the bndrun file. We will create a runtime that has +the parsii provider for testing. + + parsii.bndrun + + + . + + + + + testing + + + + + + + +To be able to resolve our own projects we need to list the dependencies we are +allowed to resolve against. + + + + org.osgi + osgi.enroute.examples.eval.parsii.provider + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.examples.eval.test + 1.0.0-SNAPSHOT + + + org.osgi + osgi.enroute.pom.distro + 2.0.0 + + + + + +## The parsii.bndrun File + +In the `bndruns` section of the bnd-testing-maven-plugin we listed the `parsii.bndrun` file. +We therefore need to create it: + + integration-test $ vi parsii.bndrun + // get the content of the next section +{: .shell } + +The content is quite similar to the bndrun file we created in the bndrun project. + + -standalone: + + -plugin.examples.eval = \ + aQute.bnd.repository.maven.pom.provider.BndPomRepository; \ + snapshotUrls=https://oss.sonatype.org/content/repositories/osgi/; \ + releaseUrls=https://repo1.maven.org/maven2/; \ + pom=${.}/pom.xml; \ + name=examples.eval; \ + location=${.}/target/cached.xml + + +We want to test the parsii provider. + + -runrequires: \ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.parsii.provider)',\ + osgi.identity;filter:='(osgi.identity=osgi.enroute.examples.eval.test)' + + -runfw: org.eclipse.osgi;version='[3.10.100.v20150529-1857,3.10.100.v20150529-1857]' + -runtrace: true + + + -runee: JavaSE-1.8 + -resolve.effective: resolve, active + + -runsystempackages.eqnx: javax.script + -runsystemcapabilities.dflt: ${native_capability} + + +Just like the bndrun file before, we need to resolve the file to find the bundles. + + integration-test $ mvn install + ... +{: .shell } + + -runbundles: \ + org.apache.felix.configadmin; version='[1.8.8,1.8.9)',\ + org.apache.felix.scr; version='[2.0.2,2.0.3)',\ + org.eclipse.equinox.metatype; version='[1.4.100,1.4.101)',\ + org.osgi.service.metatype; version='[1.3.0,1.3.1)',\ + osgi.enroute.examples.eval.parsii.provider; version='[1.0.0,1.0.1)',\ + osgi.enroute.examples.eval.test; version='[1.0.0,1.0.1)',\ + osgi.enroute.hamcrest.wrapper; version='[1.3.0,1.3.1)',\ + osgi.enroute.junit.wrapper; version='[4.12.0,4.12.1)' + + integration-test $ vi parsii.bndrun + // update the -runbundles + integration-test $ mvn install + ... + Test parsii.bndrun + Tests run : 1 + Passed : 1 + Errors : 0 + Failures : 0 + ... + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + ... +{: .shell } + +## What Did We Learn? + +In this section we created an OSGi integration test. This required a test bundle +that had the Test-Cases manifest header set. It also required an integration-test +project that ran the test. This project can run multiple integration tests by +defining multiple bndrun files and listing them in the configuration section +of the bnd-testing-maven-plugin. + + diff --git a/_tutorial_eval/490-modules.md b/_tutorial_eval/490-modules.md new file mode 100644 index 0000000..b545880 --- /dev/null +++ b/_tutorial_eval/490-modules.md @@ -0,0 +1,96 @@ +--- +title: Modules +layout: prev-next-collection +summary: Turning the parent pom into a module pom +lprev: 480-testing +lnext: 600-ci +--- + +## What you will learn in this section + +So far we've treated each project as standalone. By running Maven manually we +were responsible for building the different projects in the right order. This +clearly does not scale well; even for small projects you often find that you +compiled against a previous incarnation. + +In this section we will make our parent pom also a _module_ pom. This is a pom +that contains all our modules and will automatically build the projects in the +right order. + +Make sure you are in the top directory: + + $ cd ~/workspaces/osgi.enroute.examples.eval +{: .shell } + +## The Module POM + +Maven projects can inherit from a _parent pom_. Though it is theoretically +possible to have a different parent pom than a module pom, there seems to be a number +of problems when they are not the same. For this reason, we setup the +module project as the parent pom. + +## Adjusting Module Pom + +In the default directory (`~/osgi.enroute.examples.eval/`) we create a `pom.xml` +file. It has the following content. + + osgi.enroute.examples.eval $ vi pom.xml + // Add the module section after the `` element. +{: .shell } + +The `modules` element must fall directly under the `project` element. + + + api + simple.provider + parsii.provider + command + application + bndrun + test + integration-test + + + +## Verify + +After you created the pom.xml file you should verify that it all is ok. + + osgi.enroute.examples.eval $ mvn install + [INFO] Scanning for projects... + ... + Test parsii.bndrun + Tests run : 1 + Passed : 1 + Errors : 0 + Failures : 0 + [INFO] + [INFO] --- maven-install-plugin:2.4:install (default-install) @ osgi.enroute.examples.eval.integration-test --- + [INFO] Installing /Ws/enroute/osgi.enroute.examples.eval/integration-test/pom.xml to /Users/aqute/.m2/repository/org/osgi/osgi.enroute.examples.eval.integration-test/1.0.0-SNAPSHOT/osgi.enroute.examples.eval.integration-test-1.0.0-SNAPSHOT.pom + [INFO] ------------------------------------------------------------------------ + [INFO] Reactor Summary: + [INFO] + [INFO] osgi.enroute.examples.eval ......................... SUCCESS [ 0.252 s] + [INFO] osgi.enroute.examples.eval.api ..................... SUCCESS [ 0.808 s] + [INFO] osgi.enroute.examples.eval.simple.provider ......... SUCCESS [ 0.484 s] + [INFO] osgi.enroute.examples.eval.command ................. SUCCESS [ 0.044 s] + [INFO] osgi.enroute.examples.eval.parsii.provider ......... SUCCESS [ 0.204 s] + [INFO] osgi.enroute.examples.eval.application ............. SUCCESS [ 0.086 s] + [INFO] osgi.enroute.examples.eval.bndrun .................. SUCCESS [ 5.412 s] + [INFO] osgi.enroute.examples.eval.test .................... SUCCESS [ 0.041 s] + [INFO] osgi.enroute.examples.eval.integration-test ........ SUCCESS [ 3.019 s] + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + ... +{: .shell } + +Since the output of mvn is quite, lets say, verbose, we will only show relevant parts in the +following sections. Any skipped parts will be indicated with `...`. + +## What You've Learned + +In this section we made the parent pom also a _module_ pom. By adding a `modules` +section with the projects we created we can now build all the projects in the right +order with a single command. + \ No newline at end of file diff --git a/_tutorial_eval/600-ci.md b/_tutorial_eval/600-ci.md new file mode 100644 index 0000000..1bebfb2 --- /dev/null +++ b/_tutorial_eval/600-ci.md @@ -0,0 +1,99 @@ +--- +title: Continuous Integration +layout: prev-next-collection +summary: Build the modules in continuous integration +lprev: 490-modules +lnext: 900-eclipse +--- + +## What You Will Learn + +In this section we will use [Github] and [Travis], free services for developers +when open source, to setup a continuous integration system. + +In this section it is assumed you've an account on Github and Travis. + +## Creating a Github Repository + +So far we've created a repository but it is not yet a git repository and it is +also not yet stored on Github. + +First step is to create a repository on Github with the required name: `osgi.enroute.examples.eval`. +Once you've created this repository, we need to add it in our workspace. + +## Committing the Repository + +First we should set `.gitignore` to ignore any files we do not want to include. In general, the +following should work: + + osgi.enroute.examples.eval $ vi .gitignore + // add the following content +{: .shell } + + target/ + generated/ + */bin/ + */bin_test/ + */target/ + */generated/ + **/*.class + **/*.jar + +We now need to connect the repository to Github (replace the xxxx with your user name): + + osgi.enroute.examples.eval $ git init + osgi.enroute.examples.eval $ git add . + osgi.enroute.examples.eval $ git commit -m "first commit" + osgi.enroute.examples.eval $ git remote add origin git@github.com:xxxxxx/osgi.enroute.examples.eval.git + osgi.enroute.examples.eval $ git push -u origin master +{: .shell } + + +## Activating Travis + +After we've created the Github repository we can enable it on the Travis site (replace xxxx with your Github user id). + +* [https://travis-ci.org/xxxxx](https://travis-ci.org/xxxxx) + +You should click on your name in the right hand top corner and select `Accounts`. This +opens a list of all your repositories. If you repository does not show up, you can push +`Sync Account`. Flip the switch to on to activate your CI build. + +## Preparing for Travis + +Travis is a free for open source continuous integration solution. By adding a simple +file `.travis.yml` in the root of a Github repository we can build the workspace +automatically. + + osgi.enroute.examples.eval $ vi .travis.yml + // add the following content +{: .shell } + + language: java + + jdk: + - oraclejdk8 + + before_install: + - rm ~/.m2/settings.xml + +That is all! By default, Travis will call mvn install first without the tests and then +later it will call mvn test to verify the results. Exactly what we need. This way +we found any easy to find problems before we run long tests. + +We remove the settings because the default settings contain repositories we do not want +to use in our build. + +So now save the change and commit it. + + osgi.enroute.examples.eval $ git add .travis.yml + osgi.enroute.examples.eval $ git commit -m "Travis" + osgi.enroute.examples.eval $ git push +{: .shell } + +You can now go to the Travis site and watch our repo build: + +* [https://travis-ci.org/xxxxx/osgi.enroute.examples.eval](https://travis-ci.org/xxxxx/osgi.enroute.examples.eval) + + + diff --git a/_tutorial_eval/900-eclipse.md b/_tutorial_eval/900-eclipse.md new file mode 100644 index 0000000..f49f5bc --- /dev/null +++ b/_tutorial_eval/900-eclipse.md @@ -0,0 +1,161 @@ +--- +title: Eclipse and Bndtools +layout: prev-next-collection +lprev: 600-ci +lnext: 050-start +summary: Use Eclipse to edit debug your bundles with the Bndtools editors +--- + +## What You Will Learn in this Section + +In this section we will go through the support Eclipse provides to help you +develop bundles with maven. It shows how to use the Bndtools editors for +the bnd and bndrun files as well as how to debug your code. + +It is assumed you have setup your Eclipse and installed Bndtools. You can +look at the [Quick Start](/qs/100-prerequisites.html) how to setup your +environment. M2Eclipse is generally already installed in Eclipse. + +Though we need to install Bndtools for this section, we only use the editors +standalone. You should not make the projects Bndtools projects. + +## M2Eclipse + +If you use Maven and Eclipse then M2Eclipse is the best solution for OSGi +enRoute development. It allows you to create an Eclipse workspace with projects +that can reside anywhere on the file system. + +This tutorial clearly cannot teach M2Eclipse, there are other sources for that. + +## The bnd Editor + +If you double click on a bnd file then it will open a bnd editor when Bndtools is installed +in Eclipse. The most important tab on this editor is the Source tab. This editor +provides syntax coloring and completion as well as tool tips on instructions and +headers. An important function of the bnd editor is to notice when you use +spaces after a backslash that was intended to be used as a line ending. The +editor will recognize many error situations and warn you early. + +The Content Tab and the Description tab are mostly working when used in stand-alone +mode. In the Content tab, the imports will not be calculated if the bnd.bnd does +not reside in a Bndtools project. However, you can still drag packages to the +Private-Package or Export-Package list and/or customize imports. + +The Build, Run and Test tabs are only working in Bndtools projects. + +![bnd editor](img/bnd-editor.png) + + +## The JAR Viewer + +If you double click on a JAR file then you will open by default the Bndtools +JAR editor. This editor has a Content and a Print tab. + +The Content tab is a list of the content. You can browse the manifest and other files. + +![bnd editor](img/jar-editor-content.png) + +The Print Tab is the output of an analysis by bnd. It shows the manifest information +and DS information in more detail and more readable form. + +![bnd editor](img/jar-editor-print.png) + +## The Resolution Viewer + +If you open the `Resolution` view (`Window/Show View/Bndtools/Resolution`) then +it will track the selection. If you select a JAR file it will analyze this +JAR file and show the capabilities and requirements in a more readable form than +is used in the manifest. + +![Resolution view](img/resolution.png) + +Especially useful are the packages requirements. You can see the import range +that was calculated but also by opening up these lines you +can see the classes that caused that requirement. + +## The Bndrun Editor + +The Bndrun editor has two tabs. First there is the source tab. This tab is +basically the same as the bnd editor. It provides completion, highlighting +and detection of errors when you create malformed properties. + +The Run tab is another beast, it can be very useful for editing the bndrun +files because it shows the possible dependencies. These dependencies can be +filtered and with drag and drop added to the `-runrequires` instruction. +With a button click, this list can then be resolved. The resolution can then +be inspected to handle optionals (these are listed seperately) and to analyze +where certain bundles come from. + +The Bndrun editor can also launch the application standalone or in the debugger. +This does not support dynamic update for code changes, you still have to install +the project before you see the updates. + +### Resolving + +If you double click on the `osgi.enroute.examples.eval.bndrun` file in the `bndrun` +directory then you open the Bndrun editor. You should select the `Run` tab. + +![Resolution view](img/bndrun-editor.png) + +The list of bundles you can see in the `Browse Repos` list is calculated from the +`pom.xml` file in the `bndrun` directory. (This is the Bnd Pom Repository plugin +definition.) You can drag bundles in this list and drop them on the `Run Requirements` +list. + +The parsed result is saved in the `target/cache.xml` file +to speed things up. Changes in the `pom.xml` are detected but if you make more +overall changes you should clean the project with maven to remove the cached +file because these changes cannot be detected. +{: .note } + +Below the `Browse Repos` list you can set the framework, the execution environment +and the runtime properties. This should be self explanatory. + +Once the initial requirements are set you can click on the `Resolve` button. +This will open a window (sometimes after some time) with the resolution: + +![Resolution view](img/resolution-result.png) + +The result shows the required resources based on the initial requirements in the +top list. It then shows any _optional_ resources. These are resources that are included +because the resolution is ok without them but some resources could use them. You +can select the desired resources and then update and resolve. + +When you select a resource in the top list, the bottom list shows an explanation +why that resource is included. That is, what resources required it. + +When you click `Finish` the `-runbundles` list is updated and the bnd.bnd file +is saved. + +This works considerably faster and is more elegant than running `mvn install`, +get the new `-runbundles` and update the bndrun file. However, it does require +Eclipse. + +### Debugging + +On the right top of the Bndrun editor in the `Run` tab there are three buttons: + +* `Run OSGi` – Launch the application. +* `Debug OSGi` – Launch the defined application in the debugger +* `Export` – Export the application to an executable JAR with all its dependencies + +M2Eclipse continuously compiles and runs some of the plugins but will not +JAR the result. This means that you need to do a `mvn install` to update the +bundles in the `~/.m2/repository`. When you run your framework with support +of the Bndrun editor then you should be aware that Eclipse updates your code +in the VM when you change the source but it does not create a new bundle. The changes +you see are also only for the class files. It will not see updates to the +resources. This is a very different experience from Bndtools that synchronizes all +the updates continuously. + +This means that if you're working in Eclipse you have to be quite aware of the +state of the different projects. In general, after you've made a change you should +do a full clean install of that project. + +When you install, the Maven JAR plugin overwrites the existing file. This can +cause a core dump in the JVM. +{: .note } + + +[M2Eclipse]: http://www.eclipse.org/m2e/ + \ No newline at end of file diff --git a/_tutorial_eval/img/api-diagram.png b/_tutorial_eval/img/api-diagram.png new file mode 100644 index 0000000..842e23a Binary files /dev/null and b/_tutorial_eval/img/api-diagram.png differ diff --git a/_tutorial_eval/img/app-0.png b/_tutorial_eval/img/app-0.png new file mode 100644 index 0000000..7e979a2 Binary files /dev/null and b/_tutorial_eval/img/app-0.png differ diff --git a/_tutorial_eval/img/application-diagram.png b/_tutorial_eval/img/application-diagram.png new file mode 100644 index 0000000..19b302a Binary files /dev/null and b/_tutorial_eval/img/application-diagram.png differ diff --git a/_tutorial_eval/img/bnd-editor.png b/_tutorial_eval/img/bnd-editor.png new file mode 100644 index 0000000..2eb1591 Binary files /dev/null and b/_tutorial_eval/img/bnd-editor.png differ diff --git a/_tutorial_eval/img/bndrun-editor.png b/_tutorial_eval/img/bndrun-editor.png new file mode 100644 index 0000000..f01cdc1 Binary files /dev/null and b/_tutorial_eval/img/bndrun-editor.png differ diff --git a/_tutorial_eval/img/command-diagram.png b/_tutorial_eval/img/command-diagram.png new file mode 100644 index 0000000..84fe570 Binary files /dev/null and b/_tutorial_eval/img/command-diagram.png differ diff --git a/_tutorial_eval/img/index.png b/_tutorial_eval/img/index.png new file mode 100644 index 0000000..7fba073 Binary files /dev/null and b/_tutorial_eval/img/index.png differ diff --git a/_tutorial_eval/img/jar-editor-content.png b/_tutorial_eval/img/jar-editor-content.png new file mode 100644 index 0000000..080e9a2 Binary files /dev/null and b/_tutorial_eval/img/jar-editor-content.png differ diff --git a/_tutorial_eval/img/jar-editor-print.png b/_tutorial_eval/img/jar-editor-print.png new file mode 100644 index 0000000..f32221e Binary files /dev/null and b/_tutorial_eval/img/jar-editor-print.png differ diff --git a/_tutorial_eval/img/m2e-bundles.png b/_tutorial_eval/img/m2e-bundles.png new file mode 100644 index 0000000..c3624ba Binary files /dev/null and b/_tutorial_eval/img/m2e-bundles.png differ diff --git a/_tutorial_eval/img/overview.png b/_tutorial_eval/img/overview.png new file mode 100644 index 0000000..c3624ba Binary files /dev/null and b/_tutorial_eval/img/overview.png differ diff --git a/_tutorial_eval/img/parsii-diagram.png b/_tutorial_eval/img/parsii-diagram.png new file mode 100644 index 0000000..3f415e6 Binary files /dev/null and b/_tutorial_eval/img/parsii-diagram.png differ diff --git a/_tutorial_eval/img/provider-layout.png b/_tutorial_eval/img/provider-layout.png new file mode 100644 index 0000000..364a93a Binary files /dev/null and b/_tutorial_eval/img/provider-layout.png differ diff --git a/_tutorial_eval/img/resolution-result.png b/_tutorial_eval/img/resolution-result.png new file mode 100644 index 0000000..102e586 Binary files /dev/null and b/_tutorial_eval/img/resolution-result.png differ diff --git a/_tutorial_eval/img/resolution.png b/_tutorial_eval/img/resolution.png new file mode 100644 index 0000000..84b5a00 Binary files /dev/null and b/_tutorial_eval/img/resolution.png differ diff --git a/_tutorial_eval/img/simple-diagram.png b/_tutorial_eval/img/simple-diagram.png new file mode 100644 index 0000000..e7431e5 Binary files /dev/null and b/_tutorial_eval/img/simple-diagram.png differ diff --git a/_tutorial_eval/img/tutorial_eval.png b/_tutorial_eval/img/tutorial_eval.png new file mode 100644 index 0000000..4d5094a Binary files /dev/null and b/_tutorial_eval/img/tutorial_eval.png differ