Hello Spring MVC and OSGi

0

源起

之前已經可以將物件利用 spring+hibernet 存放道資料庫,現要整合到一個網頁服務上, 所以需要練習一下 Spring MVC 的架構,並把 MVC 納入 haha.remix.spring 中。

預計將之前的 jpa 練習的 userdao 拿來注到 controller 之中,然後在網頁中簡單列出來。

Spring OSGi Specification (v0.7) 2.5 Web application support

結果

瀏覽器開到 http://127.0.0.1/foo/index.html 可以看到資料庫輸出。

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      system.bundle_3.3.0.v20061101
5    ACTIVE      javax.servlet_2.4.0.200609281713
6    ACTIVE      javax.servlet.jsp_2.0.0.200610251427
7    ACTIVE      log4j_1.2.13
9    ACTIVE      org.apache.commons.logging_1.0.4
10    ACTIVE      org.eclipse.equinox.ds_1.0.0.v20060828
12    ACTIVE      org.eclipse.equinox.log_1.0.100.v20060717
13    ACTIVE      org.eclipse.osgi.services_3.1.100.v20060918
75    ACTIVE      org.apache.commons.el_1.0.0
76    ACTIVE      org.apache.jasper_5.5.17.200610251427
77    ACTIVE      org.eclipse.equinox.http.jetty_1.0.0.v20061012
78    ACTIVE      org.eclipse.equinox.http.servlet_1.0.0.v20061023
79    ACTIVE      org.eclipse.equinox.jsp.jasper_1.0.0.200610251427
80    ACTIVE      org.eclipse.equinox.jsp.jstl_1.0.0
81    ACTIVE      org.mortbay.jetty_5.1.11.200609281713
85    ACTIVE      haha.hello.jpa.userdao_1.0.0
87    ACTIVE      haha.remix.derby_1.0.0
88    ACTIVE      haha.remix.hibernate_1.0.0
94    ACTIVE      haha.hello.mvc.userdao_1.0.0
95    ACTIVE      haha.remix.spring_1.0.0

haha.hello.mvc.userdao

首先要先找到 HttpService 來用。

META-INF/spring/bean.xml

<osgi:reference id="httpService" interface="org.osgi.service.http.HttpService"/>
<bean name="servletRegister" class="haha.hello.mvc.userdao.ServletRegister" init-method="init">
   <property name="httpService" ref="httpService"/>
</bean>

haha.hello.mvc.userdao.ServletRegister.java

DispatcherServlet servlet = new DispatcherServlet();
servlet.setNamespace("foo-servlet");
OsgiWebApplicationContext owac = new OsgiWebApplicationContext();
servlet.setContextClass(owac.getClass());
httpService.registerServlet("/foo", servlet, null ,null);

WEB-INF/foo-servlet.xml

<osgi:reference id="userDao" interface="haha.hello.jpa.userdao.UserDao"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <value>
                /*.html=fooController
            </value>
        </property>
</bean>
<bean id="fooController" 
      class="haha.hello.mvc.userdao.FooController">
     <property name="userDao" ref="userDao"/>
</bean>

haha.hello.mvc.userdao.FooController.java

PrintWriter out = res.getWriter();
Collection users = userDao.loadUsers();
int count = 0;
for (Iterator iter = users.iterator(); iter.hasNext();) {
 User user = (User) iter.next();            
 out.println("Hello, " + user.getName() + "<BR/>");
}

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.hello.mvc.userdao
Bundle-SymbolicName: haha.hello.mvc.userdao
Bundle-Version: 1.0.0
Bundle-Activator: haha.hello.mvc.userdao.Activator
Bundle-Localization: plugin
Import-Package: haha.hello.jpa.userdao,
 javax.servlet;version="2.4.0",
 javax.servlet.http;version="2.4.0",
 org.osgi.framework;version="1.3.0",
 org.osgi.service.http;version="1.2.0",
 org.springframework.osgi.context.support,
 org.springframework.web.servlet,
 org.springframework.web.servlet.mvc

觀察

  1. 註冊的動作之前都是用 OSGi DS 做法注入,這次用 Spring 的 osgi:reference 注入,同時也將 userdao 服務注入 controller 之中練習。
  2. OsgiWebApplicationContext 提供支援將 context 注入,不然 osgi:reference 無法使用。

Hello Spring Jpa and OSGi

0

源起

之前用 jboss ejb3 包 bundle 不是很順利,於是想用 spring jpa 支援當這個用途, 試看看 EntityManager 利用 spring 注入的方式。

Chapter 12. Object Relational Mapping (ORM) data access

Getting Started With JPA in Spring 2.0

Using JPA in Spring without referencing Spring

議題

先直接用 spring-osgi 包好的 bundle 來測,不過測試過程中不是很順利,目前 spring-osgi 所用到的 bundle 很多,但是 package 相依性還沒有調好,有些需要用 eclipse buddy 方式來轉包,既然要一個個轉包 (dao/jdbc/beans..),就直接包一起變成比較大包的 haha.remix.spring。

OSGi console 結果
osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      system.bundle_3.3.0.v20061101
5    ACTIVE      javax.servlet_2.4.0.200609281713
9    ACTIVE      org.apache.commons.logging_1.0.4
10    ACTIVE      org.eclipse.equinox.ds_1.0.0.v20060828
12    ACTIVE      org.eclipse.equinox.log_1.0.100.v20060717
13    ACTIVE      org.eclipse.osgi.services_3.1.100.v20060918
18    ACTIVE      org.springframework.osgi_2.0.0
68    ACTIVE      haha.hello.jpa.userdao_1.0.0
69    ACTIVE      haha.hello.jpa.userdao.client_1.0.0
70    ACTIVE      haha.remix.derby_1.0.0
71    ACTIVE      haha.remix.hibernate_1.0.0
73    ACTIVE      haha.remix.spring_1.0.0

haha.remix.spring

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.remix.spring
Bundle-SymbolicName: haha.remix.spring
Bundle-Version: 1.0.0
Bundle-Activator: haha.remix.spring.Activator
Bundle-Localization: plugin
Import-Package: javax.persistence,
 javax.persistence.spi,
 org.apache.commons.logging,
 org.hibernate,
 org.hibernate.ejb,
 org.osgi.framework;version="1.3.0" 
Bundle-ClassPath: lib/spring-beans-2.1-20061103.020011-52.jar,
 .,
 lib/spring-dao-2.1-20061007.091527-15.jar,
 lib/spring-jpa-2.1-20061103.020011-52.jar,
 lib/spring-jdbc-2.1-20061103.020011-52.jar,
 lib/aopalliance.osgi-1.0-20061103.020011-48.jar,
 lib/spring-aop-2.1-20061103.020011-52.jar,
 lib/spring-context-2.1-20061103.020011-52.jar,
 lib/spring-core-2.1-20061103.020011-52.jar,
 lib/spring-web-2.1-20061103.020011-52.jar
Export-Package: org.aopalliance.aop,
 org.springframework.aop;version="2.1.0",
 org.springframework.aop.framework;version="2.1.0",
 org.springframework.aop.target;version="2.1.0",
 org.springframework.beans;version="2.1.0",
 org.springframework.beans.factory;version="2.1.0",
 org.springframework.beans.factory.config;version="2.1.0",
 org.springframework.beans.factory.parsing;version="2.1.0",
 org.springframework.beans.factory.support;version="2.1.0",
 org.springframework.beans.factory.xml;version="2.1.0",
 org.springframework.context;version="2.1.0",
 org.springframework.context.support;version="2.1.0",
 org.springframework.core;version="2.1.0",
 org.springframework.core.io;version="2.1.0",
 org.springframework.core.io.support;version="2.1.0",
 org.springframework.stereotype;version="2.1.0",
 org.springframework.transaction.annotation;version="2.1.0",
 org.springframework.util;version="2.1.0",
 org.springframework.util.xml;version="2.1.0",
 org.springframework.web.context;version="2.1.0",
 org.springframework.web.context.support;version="2.1.0" 

haha.remix.hibernate

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.remix.hibernate
Bundle-SymbolicName: haha.remix.hibernate
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Bundle-ClassPath: .,
 lib/hibernate3.jar,
 lib/hibernate-annotations.jar,
 lib/ejb3-persistence.jar,
 lib/dom4j-1.6.1.jar,
 lib/commons-collections-2.1.1.jar,
 lib/cglib-2.1.3.jar,
 lib/asm.jar,
 lib/jta.jar,
 lib/ehcache-1.2.jar,
 lib/hibernate-entitymanager.jar,
 lib/javassist.jar,
 lib/jboss-archive-browsing.jar,
 lib/antlr-2.7.6.jar
Import-Package: org.apache.commons.logging,
 org.osgi.framework;version="1.4.0",
 org.osgi.service.component;version="1.0.0",
 org.osgi.service.log;version="1.3.0" 
Export-Package: javax.persistence,
 javax.persistence.spi,
 org.hibernate,
 org.hibernate.ejb,
 org.hibernate.proxy
Eclipse-BuddyPolicy: registered

haha.hello.jpa.userdao

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.hello.jpa.userdao
Bundle-SymbolicName: haha.hello.jpa.userdao
Bundle-Version: 1.0.0
Bundle-Activator: haha.hello.jpa.userdao.Activator
Import-Package: javax.persistence,
 org.apache.derby.jdbc,
 org.hibernate.proxy,
 org.osgi.framework;version="1.4.0",
 org.springframework.stereotype;version="2.1.0",
 org.springframework.transaction.annotation;version="2.1.0" 
Eclipse-RegisterBuddy: haha.remix.hibernate
Export-Package: haha.hello.jpa.userdao

MEAT-INF/spring/bean.xml(部份)

<tx:annotation-driven />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<bean id="entityManagerFactory" 
  class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
...
</bean>

<bean id="transactionManager" 
   class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="userDao" class="haha.hello.jpa.userdao.UserDaoImpl"/>

<osgi:service id="osgiUserDao" ref="userDao" 
    interface="haha.hello.jpa.userdao.UserDao"/>

haha.hello.jpa.userdao.client

MEAT-INF/spring/bean.xml(部份)

<osgi:reference id="userDao" interface="haha.hello.jpa.userdao.UserDao"/>
<bean name="userDaoClient" 
    class="haha.hello.jpa.userdao.client.UserDaoClient" init-method="init">
      <property name="userDao" ref="userDao"/>
</bean>

觀察

  1. 如果沒有加上 @Transactional 的話,DAO 會遭遇 hibernate session 關閉問題。 http://forum.springframework.org/archive/index.php/t-29054.html

Hello Spring OSGi

13

源起

Spring 有廣大的用戶與部署經驗,當然也要試看看,包起來如何運作。

環境部署

因為目前文件尚未完備,bundle 之間相依性很複雜,這裡直接用 simple-service-bundle 來測試,順便看看要載入多少東西。所有的 bundle 除了 OSGi 之外,全部取自下面連結。

http://static.springframework.org/maven2-snapshots/

如何取得服務

這個 bundle 會做兩種存取 simple-service-bundle 的方式的測試,差別在生命週期掌握在誰的手上。

  1. Declarative Services (OSGi SCR)
  2. osgi:reference (Spring Ioc)

關於這兩種做法,摘錄 Spring OSGi Specification (v0.7) 的說法如下。

http://www.springframework.org/osgi/specification

Spring-OSGi enabled bundles can co-exist happily with bundles using DS, 
but for new bundle development we recommend the use of the Spring OSGi 
support in place of DS.

下面是設定檔的部份。

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.foo
Bundle-SymbolicName: haha.foo
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Import-Package: org.osgi.service.component;version="1.0.0",
 org.osgi.service.log;version="1.3.0",
 org.springframework.osgi.samples.simpleservice
Service-Component: OSGI-INF/reference.xml
Require-Bundle: org.springframework.osgi

OSGI-INF/reference.xml

<?xml version="1.0" encoding="UTF-8"?>
<component name="haha.foo.Component">
  <implementation class="haha.foo.Component"/>
  <reference name="LOG" interface="org.osgi.service.log.LogService" 
    cardinality="1..1" bind="setLog" unbind="unsetLog" policy="static"/>
  <reference name="MYSERVICE" 
    interface="org.springframework.osgi.samples.simpleservice.MyService" 
    cardinality="1..1" bind="setMyService" unbind="unsetMyService" 
    policy="static"/>
</component>

META-INF/spring/bean.xml

<osgi:reference id="myService" 
  interface="org.springframework.osgi.samples.simpleservice.MyService"/>
<bean name="foobean" class="haha.foo.Bean">
    <property name="myService" ref="myService"/>
</bean>

結果

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      system.bundle_3.3.0.v20061023
4    ACTIVE      org.eclipse.equinox.ds_1.0.0.v20060828
5    ACTIVE      org.eclipse.equinox.log_1.0.100.v20060717
6    ACTIVE      org.eclipse.osgi.services_3.1.100.v20060918
8    ACTIVE      org.apache.commons.logging_1.0.4
10    ACTIVE      log4j_1.2.13
11    ACTIVE      org.aopalliance_1.0.0
12    ACTIVE      org.springframework.aop_2.0.0
13    ACTIVE      org.springframework.beans_2.0.0
14    ACTIVE      org.springframework.context_2.0.0
15    ACTIVE      org.springframework.core_2.1.0
16    ACTIVE      org.springframework.osgi_2.0.0
20    ACTIVE      haha.foo_1.0.0
21    ACTIVE      org.springframework.osgi.samples.simpleservice_1.0.0

觀察

  1. 覺得由 spring 來存取比目前 ds 方便,spring-osgi 讓你兩種方法都可以用,很有彈性,這也代表很容易嵌入現有的 OSGi (例如 RCP) 架構中使用。

Rebundle hibernate3.2

0

源起

想要加上 ORM 這一層,同時練習一下 javax.persistence 與 EntityManager 的用法。 所以下面會包成兩包,一包是 haha.hibernate,一包是 haha.helloem。

關於資料庫的部份請參考之前作法 Rebudle Derby

部分參考連結 :

An Introduction to Java Persistence for Client-Side Developers

Hibernate EntityManager

haha.hibernate

包這個主要將相依的東西納入即可,同時匯入需要的 jdbc 等類別。

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.hibernate
Bundle-SymbolicName: haha.hibernate
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Bundle-ClassPath: .,
 lib/hibernate3.jar,
 lib/hibernate-annotations.jar,
 lib/ejb3-persistence.jar,
 lib/dom4j-1.6.1.jar,
 lib/commons-collections-2.1.1.jar,
 lib/cglib-2.1.3.jar,
 lib/asm.jar,
 lib/jta.jar,
 lib/ehcache-1.2.jar,
 lib/hibernate-entitymanager.jar,
 lib/javassist.jar,
 lib/jboss-archive-browsing.jar
Import-Package: org.apache.commons.logging,
 org.apache.derby.jdbc,
 org.osgi.framework;version="1.4.0",
 org.osgi.service.component;version="1.0.0",
 org.osgi.service.log;version="1.3.0" 
Export-Package: haha.hibernate,
 javax.persistence,
 org.hibernate.proxy
Service-Component: OSGI-INF/service.xml
Eclipse-BuddyPolicy: registered

src/haha/hibernate/HibernateDservice.java

到底要包出啥服務實在不清楚,直接做看看。

package haha.hibernate;

import javax.persistence.EntityManagerFactory;

public interface HibernateDservice {
    EntityManagerFactory getEntityManagerFactory(String name);

}

src/haha/hibernate/HibernateDserviceImpl.java

public EntityManagerFactory getEntityManagerFactory(String name) {
        return Persistence.createEntityManagerFactory(name);
    }

haha.helloem

這個包啟動時會找到 haha.hibernate 的服務,然後取出 EntityManager 來用,將一個物件放入冰箱保存。

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.helloem
Bundle-SymbolicName: haha.helloem
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Import-Package: haha.hibernate,
 javax.persistence,
 org.hibernate.proxy,
 org.osgi.framework;version="1.4.0",
 org.osgi.service.component;version="1.0.0",
 org.osgi.service.log;version="1.3.0" 
Service-Component: OSGI-INF/reference.xml
Eclipse-RegisterBuddy: haha.hibernate

META-INF/persistence.xml

<persistence-unit name="sample" transaction-type="RESOURCE_LOCAL">
      <class>haha.hibernate.demo.User</class>
      <properties>
         <property name="hibernate.connection.driver_class" 
            value="org.apache.derby.jdbc.ClientDriver"/>
         <property name="hibernate.connection.url" 
            value="jdbc:derby://localhost:1527/derbyDemoDB;create=true"/>
         <property name="hibernate.connection.username" 
            value="user1"/>
         <property name="hibernate.connection.password" 
            value="xxxxx"/>
         <property name="hibernate.dialect" 
            value="org.hibernate.dialect.DerbyDialect"/>
         <property name="hibernate.hbm2ddl.auto" 
            value="update"/>        
      </properties>
   </persistence-unit>

User object

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "usertb")
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Integer id;
  private String name;
  private Integer age;

  public User() {
  }
  // setXX and getXX ...
}

ejb3 container ?

既然包 hibernate 來當 ORM 來用,似乎用容器來支援一些操作比較方便,這些服務當然與可以自己來做,不過既有現成又測過,就來用看看。

Embeddable EJB 3.0

http://docs.jboss.org/ejb3/embedded/embedded.html

測的版本是jboss-EJB-3.0_Embeddable_ALPHA_9 ,並無法完成轉包到 OSGi 上跑,它的簡易啟動做法如下。

EJB3StandaloneBootstrap.boot(null);
EJB3StandaloneBootstrap.scanClasspath();

問題是這個 scanClasspath 是直接找 classpath 的 jar 或是目錄,而這些東西在 OSGi 環境中卻不存在,要改應該也可以,只是這樣似乎違背用既有東西減少開發的原意。

觀察

  1. EntityManager 跟 Hibernate 的 session 一樣,並非 Thread-safe,還要用些做法來幫忙,否則出錯都不知道在哪裡。
  2. 用 ejb3 container 似乎是個好方法,不過測試 jboss embedded ejb3 的方式不是很順利,該容器載入的模式是針對 jar 等固定模式,對於 osgi 的動態方式,並無法提供夠用的 java.class.path 來掃描那些 ejb3 檔案。
  3. DAO 部份也需要考慮如何實做。

Rebundle derby.jar

0

源起

應用程式總是需要地方放資料,這裡用的是 Apache Derby。因為文件提到支援 OSGi,原先預期可以直接使用,因為其內建 EmbeddedActivator,不過無法在 eclipse pde 環境載入,原因不明,於是轉包一個來用。

另一個原因是希望將 jar 包成一個,比較方便 export。

轉包

目前內容和 org.apache.derby.osgi.EmbeddedActivator 一樣如下,只是加個 MANIFEST.MF。

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.derby
Bundle-SymbolicName: haha.derby
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Bundle-ClassPath: lib/derby.jar,
 lib/derbytools.jar,
 .
Bundle-Activator: org.apache.derby.osgi.EmbeddedActivator
Import-Package: org.osgi.framework;version="1.4.0" 
Export-Package: org.apache.derby.jdbc

org.apache.derby.osgi.EmbeddedActivator

public final class EmbeddedActivator implements BundleActivator {

  public void start(BundleContext context) {
    new org.apache.derby.jdbc.EmbeddedDriver();
  }

  public void stop(BundleContext context) {
    try {
      DriverManager.getConnection("jdbc:derby:;shutdown=true");
    } catch (SQLException sqle) {
    }
  }
}

Network Server Mode

為了方便測試,將啟動改為 org.apache.derby.drda.NetworkServerControl 來負責, 啟動後預設將開啟 1527 port,可改用 org.apache.derby.jdbc.ClientDriver 來連線。

MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.derby
Bundle-SymbolicName: haha.derby
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Bundle-ClassPath: .,
 lib/derbytools.jar,
 lib/derby.jar,
 lib/derbynet.jar,
 lib/derbyclient.jar
Bundle-Activator: haha.derby.DerbyActivator
Import-Package: org.osgi.framework;version="1.4.0" 
Export-Package: org.apache.derby.jdbc

src/haha/derby/DerbyActivator.java

public void start(BundleContext context) throws Exception {
  server = new NetworkServerControl();
  server.start(null);
}
public void stop(BundleContext context) throws Exception {
  server.shutdown();
}

分開比較方便

每個包都需要功能測試,這次想要將之前的 haha.remix.derby 分兩包, 一個是 haha.remix.derby.server 負責 derby.jar/derbynet.jar/jerbytools.jar, 另一個是 haha.remix.derby.client 負責 derbyclient.jar。

觀察

  1. 資料以檔案夾為單位,例如建立 derbyDB 的話,會建立 derbyDB 目錄來放資料。 沒有設定 derby.system.home 或是直接在 jdbc 中指明位置的話,大概會出現像 是 C:\Downloads\EclipseInstall\eclipse-3.2 當前執行程式的目錄下。

Older posts: 1 2 3 4