Class Reloading-4 using Struts2 OSGi plugin without springs dependency
I am writing this blog because I did not find a tutorial of using struts2 OSGi plugin without spring dependency. The official plugin document is a bit blurred. In fact this plugin has very minimal documentation. Which shows either people found it uninteresting or are unknown to this plugin. So in order to bring some interest to this plugin and one can easily get a feel of how this works, I am writing this along with small projects in google code, which should be easy to run as soon as you checkout. These are eclipse projects and maven managed dependencies.
The main struts 2 OSGi project: http://code.google.com/p/class-reloading-test/source/browse/#svn/trunk/Struts2OSGi
The sample bundle project: http://code.google.com/p/class-reloading-test/source/browse/#svn/trunk/MyOSGI
The main project already contains 3 bundles.
MyOsgi.jar
struts2-osgi-admin-bundle-2.3.1.jar
struts2-osgi-demo-bundle-2.3.1.jar (The official demo bundle comes with spring dependency injection for ActionClass creation, which I changed to a normal action without DI, it works great)
This is my web.xml.
Note the TemplatePath variable I found this out the hard way.
<web-app id="struts_blank" version="2.4"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/j2ee"
xsi:schemalocation=
"http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts Blank Convention</display-name>
<listener>
<listener-class>view.InitListener</listener-class>
</listener>
<context-param>
<param-name>TemplatePath</param-name>
<param-value>class://</param-value>
</context-param>
<context-param>
<param-name>struts.osgi.clearBundleCache</param-name>
<param-value>false</param-value>
</context-param>
<listener>
<listener-class>
org.apache.struts2.osgi.StrutsOsgiListener
</listener-class>
</listener>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Here is my struts.properties struts.ObjectFactory tells that class reloading and instantiation is done by OSGi Note my second line is committed
struts.objectFactory=osgi
#struts.objectFactory.delegate=sprintOsgi
All the dependencies are managed by maven. So here is the pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>view</groupId> <artifactId>osgi</artifactId> <version>${project.version}</version> <packaging>war</packaging> <name>Struts 2 Blank Convention Webapp</name> <properties> <project.version>2.3.1</project.version> <struts2.version>${project.version}</struts2.version> </properties> <dependencies> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>${struts2.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-convention-plugin</artifactId> <version>${struts2.version}</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-config-browser-plugin</artifactId> <version>${struts2.version}</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.5</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>velocity</groupId> <artifactId>velocity</artifactId> <version>1.5</version> </dependency> <dependency> <groupId>velocity-tools</groupId> <artifactId>velocity-tools</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> <version>2.1</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-osgi-plugin</artifactId> <version>${struts2.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <encoding>UTF-8</encoding> <source>1.5</source> <target>1.5</target> </configuration> </plugin> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <version>6.1.21</version> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <scanTargets> <scanTarget>src/main/webapp/WEB-INF</scanTarget> <scanTarget>src/main/webapp/WEB-INF/web.xml</scanTarget> <scanTarget>src/main/resources/struts.xml</scanTarget> </scanTargets> </configuration> </plugin> </plugins> </build> </project>
If you know struts2 this should get you started. The project directory hierarchy looks like this
osgi │ .classpath │ .project │ pom.xml │ ├───.settings │ ├───src │ ├───main │ │ ├───java │ │ │ └───view │ │ │ │ InitListener.java │ │ │ │ │ │ │ └───actions │ │ │ HelloAction.java │ │ │ │ │ ├───resources │ │ │ │ log4j.properties │ │ │ │ struts.properties │ │ │ │ │ │ │ └───view │ │ │ package.properties │ │ │ package_es.properties │ │ │ │ │ └───webapp │ │ │ index.jsp │ │ │ │ │ └───WEB-INF │ │ │ appengine-web.xml │ │ │ web.xml │ │ │ │ │ ├───classes │ │ │ └───bundles │ │ │ └───2 │ │ │ MyOsgi.jar │ │ │ struts2-osgi-admin-bundle-2.3.1.jar │ │ │ struts2-osgi-demo-bundle-2.3.1.jar │ │ │ │ │ └───content │ │ hello.jsp │ │ │ └───test │ └───java │ └───view │ └───actions └───targetThis is really a struts-blank created using mvn archetype:generate for struts2 conventions example. The Action class HelloAction.class is actually empty and not really of any use. The interesting part is the folder where you can put all your bundles
WEB-INF/classes/bundles/2
Just copy the bundle there and fire up the browser
http://localhost:6060/osgi/osgi/admin/
You will see the below screen:
There is the shell for apache felix:
http://localhost:6060/osgi/osgi/admin/shell.action
Click on that, open the shell and type ps
It will show all the active bundles
$ ps
START LEVEL 3
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.4.1)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.2)
[ 2] [Active ] [ 1] Apache Felix Framework (1.4.1)
[ 3] [Active ] [ 2] MyOsgi (1.0.0.qualifier)
[ 4] [Active ] [ 2] Struts 2 OSGi Admin Bundle (2.3.1)
[ 24] [Active ] [ 2] Struts 2 OSGi Demo Bundle (2.3.1)
$ stop 3
$ uninstall 3
The number of running processed now becomes MyOsgi.
$ ps
START LEVEL 3
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.4.1)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.2)
[ 2] [Active ] [ 1] Apache Felix Framework (1.4.1)
[ 4] [Active ] [ 2] Struts 2 OSGi Admin Bundle (2.3.1)
[ 24] [Active ] [ 2] Struts 2 OSGi Demo Bundle (2.3.1)
---$ install file:///C:/Users/Samarjit/Desktop/test/MyOSGI/MyOsgi.jar
$ ps
$ start 26
$ ps
Also look at eclipse console:
Among other things you will also see the initiation logs of the MyOsgi bundle
which is in our case a simple "Hello World!!". Go ahead and change that and uninstall/install again.
Bundle ID: 26
$ ps
START LEVEL 3
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.4.1)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.2)
[ 2] [Active ] [ 1] Apache Felix Framework (1.4.1)
[ 4] [Active ] [ 2] Struts 2 OSGi Admin Bundle (2.3.1)
[ 24] [Active ] [ 2] Struts 2 OSGi Demo Bundle (2.3.1)
[ 26] [
Installed
] [ 1] MyOsgi (1.0.0.qualifier)
$ start 26
$ ps
START LEVEL 3
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.4.1)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.2)
[ 2] [Active ] [ 1] Apache Felix Framework (1.4.1)
[ 4] [Active ] [ 2] Struts 2 OSGi Admin Bundle (2.3.1)
[ 24] [Active ] [ 2] Struts 2 OSGi Demo Bundle (2.3.1)
[ 26] [
Active
] [ 1] MyOsgi (1.0.0.qualifier)
Creating Bundle MyOsgi.jar
We will now see how the bundles are created. Frankly enough I am really a new to OSGi, but I will try to create a very basic bundle, and this can be dropped in bundles folder and installed and activated. The MyOsgi project is ofcourse a basic ecclipse plugin project, created byeclipse->New project->Plugin Project
As you know eclipse is build on top of equinox OSGi, but since we are going to use Apache Felix which is bundles with the Struts2 OSGi plugin, just select the targeted runtime as standard.
Enter a project name, and select Target Platform (this project is targeted to run with:): Standard
MyOsgi │ .project │ .classpath │ build.properties │ ├───src │ └───myosgi │ Activator.java │ ├───.settings │ org.eclipse.jdt.core.prefs │ org.eclipse.pde.core.prefs │ ├───META-INF │ MANIFEST.MF │ └───bin └───myosgi Activator.classThe main entry point of any bundle is this class. This needs to be mentioned in META-INF/MANIFEST.MF file. All I have done is added in the
System.out.println()
there which gets printed when the bundle is activated or deactivated.
package myosgi; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class Activator implements BundleActivator { /* * (non-Javadoc) * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { System.out.println("Hello World!!"); } /* * (non-Javadoc) * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { System.out.println("Goodbye World!!"); } }This is how the
MANIFEST.MF
file should look like, which eclipse will create automatically. To export import packages you will need to edit this file.
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: MyOsgi Bundle-SymbolicName: MyOsgi Bundle-Version: 1.0.0.qualifier Bundle-Activator: myosgi.Activator Import-Package: org.osgi.framework;version="1.3.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.6
To create the MyOsgi.jar, export from eclipse as a normal jar, but make sure NOT to overwrite
META-INF/MANIFEST.MF
. Once that jar is exported you can extract it and check the MANIFEST.MF
file remains as it is.
That's all so far. Also note all the bundle packages that are present in You should be able to understand how to use struts2 osgi plugin, install uninstall bundles. In the project uploaded in google code, you can checkout create a few mode bundles and have fun.