OSGi – Comparing service dependency management with Blueprint (part 2)

Previous Posts:

Blueprint Overview:

  • Dependency injection framework for OSGi
  • OSGi R4.2
  • XML based configuration
  • Annotation based configuration in prototype stage (as of Jul 2013)
  • Apache Aries – implementation of Blueprint (on 1.0.0 with some visible support but not heavy)
    • 15th December 2010 Aries graduates from the incubator
    • September 2010 0.2-incubating release available, including the results of compliance tests.
    • May 2010 0.1-incubating release available

Summary of findings:

  • CON: It was much harder to get up and running with Blueprint: large number of bundles and bundle dependency issues to resolve.
  • PRO: OSGi awareness is limited to an XML config file (OSGI-INF/blueprint/config.xml).  No OSGi dependencies were needed by the Date Service API or Date Service Impl.  I added an OSGi dependency to the HelloWorldFilter in order to use the HttpService from felix which was needed to register the servlet and filter.
  • CON: Could not _easily_ get the example to work.  The servlet registered correctly to the HttpService (wired in by Blueprint).  But the DateService gave a proxy exception.  Something is still not quite right with the configuration/wiring of the DateService to the HelloWorldFilter.

Setup:

  1. Follow common setup steps listed in Part 1. Recommendation is to use a separate installation of Felix for each one of these examples to easily flip between the different wiring mechanisms.
  2. Download the Apache Aries Blueprint bundles from: http://aries.apache.org/downloads/currentrelease.html. Versions listed are the versions used at time of post.  I copied all bundle downloads to my ${FELIX_HOME}/bundle directory.  This step makes me cringe.  There’s gotta be a better way (PAX?  Using maven dependencies to pull down the artifacts and copy to the felix home?)
    • org.apache.aries.util (1.0.0) jar
    • slf4j-simple (1.7.5)
    • slf4j-api (1.7.5)
    • log4j-over-slf4j (1.7.5) jar
    • org.apache.aries.proxy (1.0.0) jar
    • org.osgi.compendium-4.2.0.jar
    • org.apache.aries.blueprint (1.1.0) jar
  3. Install and start the bundles downloaded in step 2 (slf4j-simple will only need an install since it is a bundle fragment).  Order is very important, and I’ve listed these bundles in install and start order (top to bottom)
  4. Your bundle list should look like the following:
    g! lb
    START LEVEL 1
       ID|State      |Level|Name
        0|Active     |    0|System Bundle (4.2.1)
        1|Active     |    1|Apache Felix Bundle Repository (1.6.6)
        2|Active     |    1|Apache Felix Gogo Command (0.12.0)
        3|Active     |    1|Apache Felix Gogo Runtime (0.10.0)
        4|Active     |    1|Apache Felix Gogo Shell (0.10.0)
        5|Active     |    1|Apache Felix Http Jetty (2.2.0)
        6|Active     |    1|Apache Aries Blueprint Bundle (1.1.0)
        7|Active     |    1|Apache Aries Proxy Bundle (1.0.0)
        8|Active     |    1|Apache Aries Util (1.0.0)
       13|Resolved   |    1|slf4j-simple (1.7.5)
       14|Active     |    1|log4j-over-slf4j (1.7.5)
       15|Active     |    1|slf4j-api (1.7.5)
       18|Active     |    1|osgi.cmpn (4.2.0.200908310645)
  5. Install and start the 3 bundles from your osgi-evaluation/examples/wiring/*/target/*jar
  6. Test to verify your servlet (with a response header of WIZARD) and your filter (with a response body of Hello World) and your Date Service (wired into your filter to add a formatted date to your response body) are wired together correctly to service your request.  At the time of this post, my filter was throwing an exception and not calling the DateService formattedDate() function correctly.
[~/felix] : curl localhost:8080 -v
* About to connect() to localhost port 8080 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: localhost:8080
> Accept: */*
> 
< HTTP/1.1 500 object is not an instance of declaring class
< WIZARD: JAWSOME
< Content-Type: text/html; charset=iso-8859-1
< Cache-Control: must-revalidate,no-cache,no-store
< Content-Length: 3846
< Server: Jetty(6.1.x)
< 
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 500 object is not an instance of declaring class</title>
</head>
<body><h2>HTTP ERROR 500</h2>
<p>Problem accessing /. Reason:
<pre>    object is not an instance of declaring class</pre></p><h3>Caused by:</h3><pre>java.lang.IllegalArgumentException: object is not an instance of declaring class
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.aries.proxy.impl.ProxyHandler$1.invoke(ProxyHandler.java:54)
    at org.apache.aries.proxy.impl.ProxyHandler.invoke(ProxyHandler.java:119)
    at com.sun.proxy.$Proxy5.getFormattedDate(Unknown Source)
    at org.osgiexample.filters.helloworld.HelloWorldFilter.doFilter(HelloWorldFilter.java:42)
    at org.apache.felix.http.base.internal.handler.FilterHandler.doHandle(FilterHandler.java:88)
    at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:76)
    at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:47)
    at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:33)
    at org.apache.felix.http.base.internal.dispatch.FilterPipeline.dispatch(FilterPipeline.java:48)
    at org.apache.felix.http.base.internal.dispatch.Dispatcher.dispatch(Dispatcher.java:39)
    at org.apache.felix.http.base.internal.DispatcherServlet.service(DispatcherServlet.java:67)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:390)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:926)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
</pre>
<hr /><i><small>Powered by Jetty://</small></i><br/>

Next Post:

  • Coming soon: OSGi – Comparing service dependency management with iPojo (part 3)

OSGi – Service dependency management with BundleActivator, Blueprint, Declarative Services, and iPojo (part 1)

Goals:

There are many good tutorials and wiki / blog postings on OSGi.  What I’m exploring with this post is to become more familiar with the different strategies for wiring up bundles and services within an OSGi container.

  1. Prototype a servlet filter in the Apache Felix OSGi container
    • Add a response header of “JAWSOME” with a value of “WIZARD”
    • Add a response body of “Hello World: today is ${formattedDate}”
    • Obtain the value of ${formattedDate} from an OSGi managed DateService
    • Update the DateService implementation to change the formattedDate string
  2. Evaluate service dependency management with BundleActivator, Blueprint, iPojo, Declarative Services

Setup for all Examples:

  1. Clone the git repository: https://github.com/CoderLisa/osgi-evaluation.git
  2. Build the examples in the git repository (maven and JDK are required)
  3. Download and install the latest Apache Felix Framework distribution (version 4.2.1 at time of this posting)
  4. Download and install the Apache Felix Http Jetty bundle:
    /opt/felix$java -jar bin/felix.jar 
    ____________________________
    Welcome to Apache Felix Gogo
    
    g! lb
    START LEVEL 1
       ID|State      |Level|Name
        0|Active     |    0|System Bundle (4.2.1)
        1|Active     |    1|Apache Felix Bundle Repository (1.6.6)
        2|Active     |    1|Apache Felix Gogo Command (0.12.0)
        3|Active     |    1|Apache Felix Gogo Runtime (0.10.0)
        4|Active     |    1|Apache Felix Gogo Shell (0.10.0)
    g! install file:/opt/felix/examples/setup/org.apache.felix.http.jetty-2.2.0.jar
    Bundle ID: 6
    g! start 6
    g! [INFO] Started jetty 6.1.x at port(s) HTTP:8080

OSGi Example Bundles:

You will find the code for these 3 different bundles in the osgi-evaluation/examples/wiring packages.

  1. Date Service API – a simple API that provides a formatted date string given a Date
    package org.osgiexample.service.date;
    
    import java.util.Date;
    
    public interface DateService {
        public String getFormattedDate(Date date);
    }
  2. Date Service Impl – implementation of the Date Service API
    package org.osgiexample.service.date;
    
    import java.text.DateFormat;
    import java.util.Date;
    
    public class DateServiceImpl implements DateService {
        public String getFormattedDate(Date date) {
            return DateFormat.getDateInstance(DateFormat.LONG).format(date);
        }
    }
  3. HelloWorld Filter – extends Java ServletFilter.  Registers a simple servlet and itself.  When an HTTP request is filtered, adds “Hello World” to the response with a formatted date string obtained by the DateService
    package org.osgiexample.filters.helloworld;
    
    import org.apache.felix.http.api.ExtHttpService;
    import org.osgiexample.service.date.DateService;
    
    import javax.servlet.*;
    import java.io.IOException;
    import java.util.Date;
    
    public class HelloWorldFilter implements javax.servlet.Filter {
    
        DateService dateService;
        ExtHttpService httpService;
    
        public HelloWorldFilter(DateService dateService, ExtHttpService httpService) {
            this.dateService = dateService;
            this.httpService = httpService;
    
            httpService.registerServlet("/", new SimpleServlet(), null, null);
            httpService.registerFilter(this, ".*", null, 0, null);
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            Date now = new Date();
            chain.doFilter(request, response);
            response.getWriter().write("Hello World\n" + dateService.getFormattedDate(now));
        }
    
        public void destroy() {
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    }

Next Posts:

Mountain Lion + Git + Case Insensitive filesystem == headaches

I learned this the hard way after upgrading to Mountain Lion and finding that the case insensitive filesystem was the default.  Though applications (IntelliJ, console, Finder) visually displayed to me my filenames with the characters in the case that they were created in, other applications (GIT) did not register that I had made a refactoring to the filename where the only change was the case.

What I ended up with was a git commit that included refactored classes that referenced the newly changed case in a filename, but my git commit did not pick up that the file itself had changed.  I scratched my head, said it works for me, puzzled why jenkins could not find the file when I clearly see it locally and it’s not being identified as a new change.

To get around this confusion for any development that I do, I created a partition just for my code repositories called “workspace”.  With Mountain Lion, you can run “Disk Utility” to partition your existing hard drive.  Define your new partition, use the “Mac OS Extended (Case-sensitive, Journaled)” format, and ensure that all code/development work is done on this case sensitive aware partition.

One less headache.

diskutility

Cucumber Goodness

I manufacture my own fairy dust. It’s a special blend of Cucumber goodness and a few of my own secret ingredients. I have to admit though, it’s not a perfect formula. I try to sprinkle the stuff on everyone I come into contact with on my project. Some people are able to shake it off pretty easily. Others can’t imagine how they thrived before without it.

In my magic kingdom we are using Cucumber to breakdown our story into scenarios. It’s a process that we do before development that we call our BDD sessions where we pull in the QA, Developer, Dev Lead, and BA to outline our scenario titles and define our Given, When, Then steps. During this session we uncover all sorts of goodness:

- spikes for architecture concerns
- questions for the PO to clarify requirements
- shared understanding and interpretation of the story
- tasks / questions for the developer owning the story to address

After the session, we publish our feature file as a pull request in GitHub where the entire team has an opportunity to review. Our BA creates a task for each scenario, and works through any open questions. Developers work through each scenario, moving the scenario task to completed once the code and tests have both been completed. In my ideal world (the fairy dust formula and our Continuous Delivery isn’t quite strong enough yet), after every scenario is completed our QA tests and provides immediate feedback.

If our QA finds a defect, it typically identifies a scenario that we missed during our BDD sessions. We create a new scenario for the defect, and we’re more likely to consider that scenario in future BDD sessions.

As a Dev Lead the biggest benefits that I receive are:

- Dev Done is when all scenarios have been completed. This definition doesn’t change based on the developer.
- Requirements live alongside the source code. No hunting through old project tickets or emails containing excel spreadsheets.
- High confidence level that new development isn’t breaking existing features.

Having lived in my magic kingdom for over a year now, I have run into a few trolls:

- Higher ramp up time for new developers
- More time spent on test code for some Devs than actual code
- minimal Cucumber tooling and IDE support
- initial reluctance of BAs to give up their excel spreadsheets

I’m pretty happy in my kingdom but am always looking for ways to improve. Next goal is how to get closer to CD.

IntelliJ IDEA – enable Java 1.4 assertions for Junit Run/Debug configurations

I recently ran into a test discrepancy where my test was failing when running it via the maven surefire plugin, yet it passed when I ran it through IntelliJ’s JUnit runner.

After spending a ridiculous amount of time on this issue, I was finally able to pinpoint the discrepancy to the Java 1.4 introduced “assert” statement and the differences in the default behavior of the maven surefire plugin and the IntelliJ JUnit runner.  The maven surefire plugin (v 2.7.2) enables JVM assertions by default.  IntelliJ’s JUnit runner does not (v 9.0.4).  My test in question happened to be failing due to a failing assert in an included xml parsing library.

To change the default behavior of the JUnit runner, edit the Run/Debug configurations, select Edit Defaults, select JUnit, and add “-ea” or “-enableassertions” to the VM parameters.