I have created my first OSGi service and was trying to deploy it on Apache Felix. When I looked at the system console afterwards I saw that the service was not active and some problems occured:
org.springframework.ws.client.core,version=[2.1,3) -- Cannot be resolved
org.springframework.ws.soap,version=[2.1,3) -- Cannot be resolved
org.springframework.ws.soap.axiom,version=[2.1,3) -- Cannot be resolved
org.springframework.ws.soap.saaj,version=[2.1,3) -- Cannot be resolved
So I looked which jar contained these packages and this comes from spring-ws-core-2.1.2.RELEASE.jar, which is also an OSGi bundle. I deployed that one as well but then again the following error messages occurred:
org.springframework.web.servlet,version=[3.1.0, 4.0.0) -- Cannot be resolved
Again a dependency, this time on spring-webmvc-3.2.17.RELEASE.jar. The problem however is that this one is not an OSGi bundle, how do I solve this issue then? Since it is a third party library there is not much I can think of.
So how can I use non bundle jars in an OSGi container? And how can I automatically resolve the dependency tree to not having to resolve everything by hand?
I have created the osgi-run project to address this very issue of solving bundles dependencies using the standard Maven dependency resolution (instead of OBR which is not widely supported, unfortunately), supported by Gradle.
However, the Spring jars are a terrible nightmare to resolve since the Spring Project abandoned support for OSGi, several years ago.
In theory, with osgi-run, you should be able to create a OSGi environment containing the spring-ws-core bundle by using the following gradle file:
plugins {
id "com.athaydes.osgi-run" version "1.5.1"
}
repositories {
mavenLocal()
jcenter()
}
dependencies {
osgiRuntime 'org.springframework.ws:spring-ws-core:2.1.1.RELEASE'
}
This relies on the information in the poms being consistent. And if any non-bundle is found, it is automatically converted into a OSGi bundle (see wrapping jars).
However, this is not working... Gradle can print the dependency hierarchy for the spring-ws-core jar, and here's what I get when I use that:
+--- org.springframework.ws:spring-ws-core:2.1.1.RELEASE
| +--- org.springframework.ws:spring-xml:2.1.1.RELEASE
| | +--- org.springframework:spring-context:3.1.2.RELEASE
| | | +--- org.springframework:spring-aop:3.1.2.RELEASE
| | | | +--- aopalliance:aopalliance:1.0
| | | | +--- org.springframework:spring-asm:3.1.2.RELEASE
| | | | +--- org.springframework:spring-beans:3.1.2.RELEASE
| | | | | \--- org.springframework:spring-core:3.1.2.RELEASE
| | | | | +--- org.springframework:spring-asm:3.1.2.RELEASE
| | | | | \--- commons-logging:commons-logging:1.1.1
| | | | \--- org.springframework:spring-core:3.1.2.RELEASE (*)
| | | +--- org.springframework:spring-beans:3.1.2.RELEASE (*)
| | | +--- org.springframework:spring-core:3.1.2.RELEASE (*)
| | | +--- org.springframework:spring-expression:3.1.2.RELEASE
| | | | \--- org.springframework:spring-core:3.1.2.RELEASE (*)
| | | \--- org.springframework:spring-asm:3.1.2.RELEASE
| | +--- commons-logging:commons-logging:1.1.1
| | +--- org.springframework:spring-core:3.1.2.RELEASE (*)
| | \--- org.springframework:spring-beans:3.1.2.RELEASE (*)
| +--- org.springframework:spring-context:3.1.2.RELEASE (*)
| +--- org.springframework:spring-aop:3.1.2.RELEASE (*)
| +--- org.springframework:spring-oxm:3.1.2.RELEASE
| | +--- commons-lang:commons-lang:2.5
| | +--- org.springframework:spring-beans:3.1.2.RELEASE (*)
| | +--- org.springframework:spring-context:3.1.2.RELEASE (*)
| | \--- org.springframework:spring-core:3.1.2.RELEASE (*)
| +--- org.springframework:spring-web:3.1.2.RELEASE
| | +--- aopalliance:aopalliance:1.0
| | +--- org.springframework:spring-beans:3.1.2.RELEASE (*)
| | +--- org.springframework:spring-context:3.1.2.RELEASE (*)
| | \--- org.springframework:spring-core:3.1.2.RELEASE (*)
| +--- org.springframework:spring-webmvc:3.1.2.RELEASE
| | +--- org.springframework:spring-asm:3.1.2.RELEASE
| | +--- org.springframework:spring-beans:3.1.2.RELEASE (*)
| | +--- org.springframework:spring-context:3.1.2.RELEASE (*)
| | +--- org.springframework:spring-context-support:3.1.2.RELEASE
| | | +--- org.springframework:spring-beans:3.1.2.RELEASE (*)
| | | +--- org.springframework:spring-context:3.1.2.RELEASE (*)
| | | \--- org.springframework:spring-core:3.1.2.RELEASE (*)
| | +--- org.springframework:spring-core:3.1.2.RELEASE (*)
| | +--- org.springframework:spring-expression:3.1.2.RELEASE (*)
| | \--- org.springframework:spring-web:3.1.2.RELEASE (*)
| +--- wsdl4j:wsdl4j:1.6.1
| +--- commons-logging:commons-logging:1.1.1
| +--- org.springframework:spring-core:3.1.2.RELEASE (*)
| \--- org.springframework:spring-beans:3.1.2.RELEASE (*)
I thought there could be some bug in the dependency resolution because it seems that Spring 2 jars are mixed up with Spring 3 jars, according to the resolved dependencies graph above! But no... it's exactly right.
But anyway, I got this working after some investigation...
First of all, I noticed that Spring AOP did not resolve because of its org.aopalliance.aop
requirement.
This should come, obviously, from the aopalliance jar (which is not a bundle, at least not the one in Maven Central/JCenter).
I added this instruction inside the runOsgi
block in the Gradle file, so that I could see how osgi-run
wrapped the jar into a bundle:
wrapInstructions {
printManifests = true
}
Running gradle clean createOsgi
again, the manifest gets printed... it looks like this:
--------------------------------- Manifest for aopalliance-1.0.jar ---------------------------------
Manifest-Version: 1.0
Bundle-SymbolicName: aopalliance
Bundle-ManifestVersion: 2
Bnd-LastModified: 1474120107912
Import-Package: org.aopalliance.aop
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.3))"
Tool: Bnd-3.1.0.201512181341
Ant-Version: Apache Ant 1.5.4
Originally-Created-By: 1.4.2_01-b06 (Sun Microsystems Inc.)
Export-Package: org.aopalliance.aop,org.aopalliance.intercept;uses:="o
rg.aopalliance.aop"
Bundle-Version: 1.0.0
Bundle-Name: aopalliance
Created-By: 1.8.0_60 (Oracle Corporation)
----------------------------------------------------------------------------------------------------
Notice that there is a correctly generated Bundle-Version
, but the packages are not exported with that version... by adding the instruction below, we can force the packages to be exported with a version:
wrapInstructions {
printManifests = true
manifest( 'aopalliance.*' ) {
instruction 'Export-Package', '*;version=1.0'
}
}
Now, the Export-Package
instruction in the manifest is correct:
Export-Package: org.aopalliance.aop;version="1.0",org.aopalliance.inte
rcept;version="1.0";uses:="org.aopalliance.aop"
And running the OSGi container, we see that the Spring AOP bundle still does not resolve, but now the only problem is because its (&(osgi.wiring.package=org.apache.commons.logging)(version>=1.1.1)(!(version>=2.0.0)))
requirement is not satisfied.
The commons.logging
jar has a known issue (documented in the osgi-run
README page)... it declares optional dependencies, making it hard to wrap automatically.
But additionally, the commons.logging
jar has an incorrect Specification-Version
in the manifest. It says 1.0
instead of 1.1.1
, and that's what osgi-run
uses for the bundle version, so Bundle-Version
gets the incorrect value.
By also forcing osgi-run
to export the packages with the correct version, the wrapping works correctly and the Spring AOP starts up correctly:
manifest( /commons-logging.*/ ) {
instruction 'Import-Package', '!javax.servlet,!org.apache.*,*'
instruction 'Export-Package', '*;version=1.1.1'
}
Now, moving on to the next issue, we notice that org.springframework.web
does not resolve because of its requirement on (&(osgi.wiring.package=javax.servlet)(version>=2.4.0)(!(version>=4.0.0)))
.
This one is easy, add the servlet-api bundle (provided by Felix) to the OSGi runtime and it should work... Just add this dependency in the Gradle file:
osgiRuntime 'org.apache.felix:org.apache.felix.http.servlet-api:1.1.2'
Now, the OSGi environment starts up without any failures!
Here's the full set of bundles installed:
ID|State |Level|Name
0|Active | 0|System Bundle (5.4.0)|5.4.0
1|Active | 1|aopalliance (1.0.0)|1.0.0
2|Active | 1|Commons Lang (2.5.0)|2.5.0
3|Active | 1|Jakarta Commons Logging (1.0.0)|1.0.0
4|Active | 1|Apache Felix Gogo Command (0.16.0)|0.16.0
5|Active | 1|Apache Felix Gogo Runtime (0.16.2)|0.16.2
6|Active | 1|Apache Felix Gogo Shell (0.12.0)|0.12.0
7|Active | 1|Apache Felix Servlet API (1.1.2)|1.1.2
8|Active | 1|Spring AOP (3.1.2.RELEASE)|3.1.2.RELEASE
9|Active | 1|Spring ASM (3.1.2.RELEASE)|3.1.2.RELEASE
10|Active | 1|Spring Beans (3.1.2.RELEASE)|3.1.2.RELEASE
11|Active | 1|Spring Context (3.1.2.RELEASE)|3.1.2.RELEASE
12|Active | 1|Spring Context Support (3.1.2.RELEASE)|3.1.2.RELEASE
13|Active | 1|Spring Core (3.1.2.RELEASE)|3.1.2.RELEASE
14|Active | 1|Spring Expression Language (3.1.2.RELEASE)|3.1.2.RELEASE
15|Active | 1|Spring Object/XML Mapping (3.1.2.RELEASE)|3.1.2.RELEASE
16|Active | 1|Spring Web (3.1.2.RELEASE)|3.1.2.RELEASE
17|Active | 1|Spring Web Servlet (3.1.2.RELEASE)|3.1.2.RELEASE
18|Active | 1|Spring Web Services Core (2.1.1.RELEASE)|2.1.1.RELEASE
19|Active | 1|Spring XML (2.1.1.RELEASE)|2.1.1.RELEASE
20|Active | 1|tomcat-servlet-api (8.0.0)|8.0.0
21|Active | 1|JWSDL (1.2.0)|1.2.0
And in case you want to try osgi-run
, this is the Gradle file I used:
plugins {
id "com.athaydes.osgi-run" version "1.5.1"
}
repositories {
mavenLocal()
jcenter()
}
dependencies {
osgiRuntime 'org.springframework.ws:spring-ws-core:2.1.1.RELEASE'
osgiRuntime 'org.apache.felix:org.apache.felix.http.servlet-api:1.1.2'
}
runOsgi {
wrapInstructions {
printManifests = true
manifest( 'aopalliance.*' ) {
instruction 'Export-Package', '*;version=1.0'
}
manifest( /commons-logging.*/ ) {
instruction 'Import-Package', '!javax.servlet,!org.apache.*,*'
instruction 'Export-Package', '*;version=1.1.1'
}
}
}
Just save this in a build.gradle
file, then run gradle createOsgi
to get your starter script at build/osgi/run.sh
.
I will work on osgi-run
to try to make things like these get resolved automatically, hopefully in the next version, the first build file shown above will work without further ado.