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) 架構中使用。

Comments

  1. lionger said 18 days later:
    你在javaword发表的haha.foo那个测试我已经拜读过了,非常感谢。另外想向你请教一个困扰多时的问题。就是spring-osgi中,applicaion context是在bundle启动的时候自动装载的,那么我如何获取在spring/bean.xml中定义的bean的实例呢,能给出一些指导语句吗?谢谢。 就是如你在bean.xml中定义的 。。。 那么在bundle内如果类似ctx.getBean("foobean")来获取这个bean 我的email:lionger@163.com,你可回复我邮箱,多谢
  2. LIN said 19 days later:
    就我目前了解,需要將這個 bean 變成 OSGi 的服務並發布,利用 spring-osgi 的 osgi:service 可以做到。這樣你就可以在別的 bundle 中用 osgi:reference 取出,而不需要找到 application context。
  3. lionger said 19 days later:
    关于bundle间的bean的注入确实是这样的。spring-osgi-core统一管理所有bundle中的application context,所以可以自动建立bundle间的这种osgi:reference 。而一个bundle内部应该也有从application context获取自己配置的bean实例的需要吧?既然ctx是会由spring-osgi统一自动创建的,我想应该有某个方法可以获取到吧。在其他bundle获取另一个bundle的ctx,可以利用类如bundle-id-springApplicationContext的名称获取服务,那获取自己bundle内的是否也可以使用这个方法呢?我会去实验一下,希望有知道的人也可以指导。多谢
  4. lionger said 20 days later:
    启动osgi后,运行 osgi bundle 2148 initial@reference:file:F:/Java/OSGI/haha.foo-2006-1103/haha.foo/ [2148] Id=2148, Status=ACTIVE Data Root=E:\Workspace\spring-osgi\.metadata\.plugins\org.eclipse.pde.core\Simple Service\org.eclipse.osgi\bundles\2148\data No registered services. Services in use: 发现竟然没有注册服务,同样我运行 osgi bundle 2362 initial@reference:file:E:/Workspace/spring-osgi/spring-osgi/samples/simple-service/simple-service-bundle/ [2362] Id=2362, Status=ACTIVE Data Root=E:\Workspace\spring-osgi\.metadata\.plugins\org.eclipse.pde.core\Simple Service\org.eclipse.osgi\bundles\2362\data Registered Services {org.springframework.context.ApplicationContext}={org-springframework-context-service-name=org.springframework.osgi.samples.simpleservice-springApplicationContext, service.id=36} 发现注册了ApplicationContext服务,却没有发现在spring/simpleervice-osgi.xml中定义的服务。能帮我想一下是为什么吗?thx
  5. LIN said 21 days later:
    實際情形不清楚,不過之前在 eclipse 上用,有時候會出現註冊不到的問題,起因要註冊的的服務的 bundle 比 spring-osgi bundle 先啟動完成,於是沒被 spring-osgi 動到,這點還有待研究。修正方式去調 start-level 或是改 import package 為 require package 或是整個 equniox 的執行刪掉,再重建一個新的,總之目前這個應用還不成熟,只能多試試。
  6. LIN said 21 days later:
    至於你談到關於自己 bundle 中 ctx.getBean 的事情,現在都直接注入到 bean 來取用,沒有直接 getBean 抓出來,似乎在 unit test 才會動到,有機會再試看看。
  7. lionger said 33 days later:
    你好,lin,又来向你请教问题了:假设有一个类BeanService,没有实现任何接口。利用spring-osgi发布为一个服务 这样是没有问题的。可是我在另外的bundle引用这个服务时抛错了。 错误如下: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanService': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: com.BeanService is not an interface Caused by: java.lang.IllegalArgumentException: com.BeanService is not an interface 错误的产生是在at org.springframework.osgi.service.OsgiServiceProxyFactoryBean.constructProxy(OsgiServiceProxyFactoryBean.java:242)。好像是使用什么动态代理。 如果是这样的话,如果想把一些没有实现接口的类,发布为service来提供服务,就无法利用spring-osgi了,则能使用DS中的定义方法了?
  8. lionger said 33 days later:
    注册服务:osgi:service id="beanServiceOSGI" ref="beanService" interface="com.BeanService" 、 应用服务 osgi:reference id="beanService" interface="com.BeanService"/
  9. LIN said 34 days later:
    確實是 DS 可以支援 class 直接當服務,不過規格也不建議這樣做, 參考 OSGi 規格 112 Declarative Services Specification 的 112.4.6 Service Element 部份。關於 XML 設定中 service/provide[@interface] 是可以寫 class 名字,但是也提到應該用 interface 為佳。

    這種規格書可能 會讓部份支援規格的 OSGi DS 平台,只考慮做到 interface 支援,建議還是 儘可能擠出個 interface 來用比較可靠。

    interface – The name of the interface that this service is registered under. This name must be the fully qualified name of a Java class. For example, org.osgi.service.log.LogService. The specified Java class should be an interface rather than a class, however specifying a class is supported.
  10. lionger said 34 days later:
    多谢Lin的讲解。我原来的打算是在一个bundle里面创建一个org.springframework.orm.hibernate3.LocalSessionFactoryBean并发布为服务,这样,其他bundle需要使用spring-hibernate中的功能的时候,直接使用这个sessionFactory服务。不过既然无法将未实现接口的类直接申明为服务,所以我就把dataSource发布为服务,其他模块需要使用到spring-hibernate功能,则ref这个dataSource,创建一个新的sessionFactory。 ?bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"? ?property name="dataSource"?ref bean="dataSource"/??/property? 说得比较乱,希望你能明白我的意思。请问你有没有其他方法来实现这种意图?
  11. lionger said 34 days later:
    另外请教另一个问题。就是利用applicationContext.xml文件定义bean的时候,经常会出现NoClassDefFoundError异常。比如我上面写的声明sessionFactory,就会抛出java.lang.NoClassDefFoundError: org.hibernate.cfg.Configuration,但我在bundle中的代码里,是不会抛出这样的异常的。 无论我是import进来org.hibernate.cfg,还是利用classpath定义hibernate3.jar都会出现这种问题。是不是spring在创建bean的时候,classloader和bundle的classloader不一样啊?
  12. LIN said 35 days later:
    不確定問題是否一樣,不過之前練習 hibernate bundle 需要加上 Eclipse-BuddyPolicy: registered, 要用的那一端需要 Eclipse-RegisterBuddy: haha.remix.hibernate 之類的,才能讓 hibernate 找到。

    http://blog.extremepattern.com/articles/2006/11/07/hello-spring-jpa-and-osgi
  13. LIN said 35 days later:
    還沒有練習到多個 bundle 共用一個 LocalSessionFactoryBean 的做法,有機會再試試。

    暫時想到三種做法,一是轉包 extends LocalSessionFactoryBean implements MyOrmInterface, 用 osgi:reference interface=MyOrmInterfce 來做。

    第二種是用 org.springframework.beans.factory.FactoryBean 來當 interface,不過擔心會找到 一堆這種服務,不見得是 LocalSessionFactoryBean,除非只有一個這種服務。

    第三種是 Spring OSGi Specification 中提到卻沒有說明的 osgi:reference[@filter], 查了一下 org.springframework.osgi.config.ReferenceBeanDefinitionParser 程式碼, 其中還沒有處理 filter 的部份,所以這種做法可能還要等。

(leave url/email »)

   Preview comment