Hello Small World

0

源起

在 Wikipedia 上看到一篇 Newman 的文章,講到網路的數學,主要談分析 social network 資料與建構數學模型。於是想要看看如何在 java 中玩一下。

分析這類網路往往想知道重點 (centrality measures) 所在,簡單的 (degree centrality)看連線多的。越多連線,越重要,越值得推薦。

問題在於連線的對象也應該納入考慮,連到大尾(或是比較喜歡)的,應該比較值得推薦,於是出現特徵向量評估方式 eigenvector centrality。文中還提到另外以路徑 Path 評估網路(重)點的方法。

觀察網路慢慢會發現一種特殊的現象,就是某種網路節點之間的聯繫遠比預期的還要緊密,路邊轉角賣吃的或是太空中的太空人可能是你朋友的朋友或是親戚。

提到這個現象,都會說到 Stanley Milgram 的寄信給陌生人實驗,找個朋友請他轉寄,看看經過幾手可以轉寄到該陌生人手上,觀察結果大都不超過六次,可以看出人際關係的網路遠比一般人認為的還要緊密。

http://en.wikipedia.org/wiki/Eigenvector_centrality

http://en.wikipedia.org/wiki/Social_network

http://en.wikipedia.org/wiki/Small-world_network

http://www.cs.cornell.edu/home/kleinber/

JUNG

Java Universal Network/Graph Framework 是個功能很多的工具,提供兩種世界真小的兩種模擬網路 KleinbergSmallWorldGenerator 跟 WattsBetaSmallWorldGenerator。

很方便的可以掛到 Swing 輸出。

WattsBetaSmallWorldGenerator gen = 
  new WattsBetaSmallWorldGenerator(20, 0.1 , 6);
Graph g = (Graph) gen.generateGraph();
JFrame jf = new JFrame();
VisualizationViewer vv = new VisualizationViewer(new SpringLayout(g),
  new PluggableRenderer());
jf.getContentPane().add(vv);        
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);

或是輸出 PNG 來看看。

VisualizationViewer vv = new VisualizationViewer(new SpringLayout(g),
  new PluggableRenderer(), new Dimension(600,600));
vv.setSize(600,600);
Container c = new Container();
BufferedImage bi = new BufferedImage(600,600,BufferedImage.TYPE_INT_RGB); 
Graphics2D gr = bi.createGraphics();
c.addNotify(); 
c.add(vv); 
c.setVisible(true); 
c.paintComponents(gr); 
ImageIO.write(bi, "png", new File("testdata/smallworld.png"));

結果出現在下面,應該可以再調。

觀察

  1. 畫出的網路很緊,要如何用等下次有機會再多看書。
  2. 數學的知識在畢業時沒帶走,都留給學校了。

Show Me the OSGi Bundles

0

源起

想要從 web/http 介面看到 OSGi 平台的 bundle 狀態,進一步也可以做 start/stop/update 控制。

JMX ?

一開始想到 JMX 轉 Http 的實做,既然用 equinox 就考慮 Resource Monitoring 專案看看, 該案目前還在孕育階段,牽涉的包非常多,請參閱下面連結。

http://www.eclipse.org/equinox/incubator/monitoring/index.php

目前只有小小意圖,簡單的秀出狀態的話,要調整並安裝這些包似乎太麻煩。於是自己來個小包,用 spring-osgi/spring-mvc 支援,實做一個 controller ,支援注入 BundleContext 的 BundleContextAware 介面,然後簡單秀出來看。

為了重複使用方便,將之前練習的 haha.hello.jetty 改為 haha.osgi.jetty。

haha.osgi.webconsole

使用之前做的 haha.osgi.jetty.HttpService ,只要設定根目錄,會自行找到 WEN-INF/web.xml 並啟動需要的 controller,所以這裡不寫註冊 Servlet 的程式, 改回原來習慣的 web.xml 方式。

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.osgi.webconsole
Bundle-SymbolicName: haha.osgi.webconsole
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Import-Package: javax.servlet;version="2.4.0",
 javax.servlet.http;version="2.4.0",
 org.osgi.framework;version="1.3.0" 
Require-Bundle: haha.remix.spring,
 haha.osgi.jetty

META-INF/spring/webconsole-beans.xml

<osgi:reference id="httpService" 
  interface="haha.osgi.jetty.HttpService" />

<bean id="register" 
  class="haha.osgi.jetty.Register" init-method="init" 
  destroy-method="destroy">
  <property name="httpService" ref="httpService" />
  <property name="webappDir" value="webapp" />
  <property name="contextPath" value="/osgi" />
</bean>
webapp/WEB-INF/web.xml
<servlet>
<servlet-name>status</servlet-name>
<servlet-class>
  org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
  <param-name>contextClass</param-name>
  <param-value>
  org.springframework.osgi.context.support.OsgiWebApplicationContext
  </param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>status</servlet-name>
<url-pattern>/status</url-pattern>
</servlet-mapping>

webapp/WEB-INF/status-servlet.xml

<bean
  class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
  <value>/*=statusController</value>
</property>
</bean>
<bean id="statusController" 
  class="haha.osgi.webconsole.StatusController">
</bean>

haha.osgi.webconsole.StatusController

public class StatusController extends AbstractController 
  implements BundleContextAware {

  @Override
  protected ModelAndView handleRequestInternal(HttpServletRequest req,
    HttpServletResponse res) throws Exception {

  res.setContentType("text/plain; charset=UTF-8");
  PrintWriter out = res.getWriter();
  Bundle[] bundles = bundleContext.getBundles();
  for (Bundle bundle : bundles) {
    int state = bundle.getState();
    String status = "ACTIVE";
    switch (state) {
      case Bundle.INSTALLED:
        status = "INSTALLED";
        break;
    [SKIP]

結果

http://127.0.0.1/osgi/status

Bundle 0 / system.bundle / ACTIVE
Bundle 1 / haha.remix.jetty / ACTIVE
Bundle 2 / haha.remix.spring / ACTIVE
Bundle 5 / org.apache.commons.logging / ACTIVE
Bundle 6 / org.eclipse.equinox.common / ACTIVE
Bundle 7 / org.eclipse.osgi.services / ACTIVE
Bundle 14 / haha.osgi.webconsole / ACTIVE
Bundle 16 / org.eclipse.equinox.log / ACTIVE
Bundle 18 / haha.osgi.jetty / ACTIVE

觀察

  1. 簡單使用 BundleContext 秀狀態,可以進一步做控制的實做
  2. 這類應用要設定三個 XML 似乎過於複雜,沒用到 spring-mvc 的功能,只是簡單 controller 其實可以用 servlet 代替。
  3. 轉換成 JSTL view 發生找不到 tld 的問題,變成要自帶 WEB-INF/c.tld 並改 uri 的做法,採用 eclipse buddy 機制也許也有用。主要是找不到放在別的包中的 META-INF 目錄下的 c.tld 等檔案。

Spring-driven or OSGi-standard HttpService

0

源起

原來的 OSGi HtppService 是個相當簡潔的介面,不提供太多細部的設定考量,也就是說整個 HttpService 的某些特質取決於底層的實做預設值,並不能在註冊時加以調整。

舉 welcome file 的例子來說,不同實做,出現的行為是 404/500 都不確定,太粗的介面提供小型裝置的容易實現性,但是對習慣開發 server-side 的開發者而言,實在不方便。

equinox 用 jetty 當 servlet 2.4 實做,jetty 5 是個成熟的實做,許多企業級應用都可找到,equinox 採用 ProxyServlet 轉接方式,來轉出符合 OSGi HttpService 的介面。

這裡不去修改這個 ProxyServlet 機制,而是動手另外實做類似 org.eclipse.equinox.http.jetty 的 Spring-driven HttpService 功能,同時轉用 jetty 6.1 來測試,這裡用 haha.remix.jetty 來包一大包,另外用 haha.hello.jetty 來啟動 jetty server,用 haha.hello.httpdoc 來註冊 http://localhost/。

haha.remix.jetty

不需啟動,基本上是個 lib 為主的 bundle。

META-IMF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Jetty Plug-in
Bundle-SymbolicName: haha.remix.jetty
Bundle-Version: 6.1.0
Bundle-Localization: plugin
Import-Package: org.osgi.framework;version="1.3.0" 
Bundle-ClassPath: .,
 lib/jetty-6.1.0pre2.jar,
 lib/jetty-util-6.1.0pre2.jar,
 lib/servlet-api-2.5-6.1.0pre2.jar
Export-Package: javax.servlet;version="2.5.0",
 javax.servlet.http;version="2.5.0",
 javax.servlet.resources;version="2.5.0",
 org.mortbay.component,
 org.mortbay.io,
 org.mortbay.io.bio,....[SKIP]

haha.hello.jetty

META-IMF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.hello.jetty
Bundle-SymbolicName: haha.hello.jetty
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Import-Package: org.osgi.framework;version="1.3.0" 
Require-Bundle: haha.remix.spring,
 haha.remix.jetty
Bundle-ClassPath: .
Export-Package: haha.hello.jetty

將 jetty 用 osgi-service 註冊到 OSGi 環境去。

MEAT-INF/spring/jetty-beans.xml

<bean id="httpServiceImpl" 
  class="haha.hello.jetty.HttpServiceImpl" 
 init-method="init" destroy-method="destroy">
  <property name="port" value="80" />
  <property name="debugEnable" value="true" />
</bean>
<osgi:service ref="httpServiceImpl" interface="haha.hello.jetty.HttpService"/>

haha.hello.jetty.HttpService

public interface HttpService {
  void addWebApp(URL fileURL, String contextPath);
}

haha.hello.httpdoc

這是比較麻煩的地方,主要是轉換檔案位置的問題,這裡用到 org.eclipse.equinox.common 的支援, 使用 org.eclipse.core.runtime.FileLocator 來轉檔案的位置給 haha.hello.jetty 知道。

META-IMF/MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.hello.httpdoc
Bundle-SymbolicName: haha.hello.httpdoc
Bundle-Version: 1.0.0
Bundle-Localization: plugin
Import-Package: javax.servlet;version="2.4.0",
 javax.servlet.http;version="2.4.0",
 org.osgi.framework;version="1.3.0",
 org.osgi.service.component;version="1.0.0",
 org.osgi.service.log;version="1.3.0",
 org.springframework.osgi.context
Require-Bundle: org.eclipse.equinox.common,
 haha.hello.jetty

將目錄所在用 osgi-reference 註冊到 jetty 環境去。

MEAT-INF/spring/httpdoc-beans.xml

<osgi:reference id="httpService" 
  interface="haha.hello.jetty.HttpService" />

<bean id="register" 
  class="haha.hello.httpdoc.Register" init-method="init" 
  destroy-method="destroy">
  <property name="httpService" ref="httpService" />
</bean>

haha.hello.httpdoc.Register 須實做 BundleContextAware 讓 Spring-OSGi 注入 BundleContext 來用。

public class Register implements BundleContextAware{

 private HttpService httpService;
 private BundleContext bundleContext;

 public void init() throws IOException{
  Bundle bundle = bundleContext.getBundle();
  URL entry = bundle.getEntry("htdoc");
  URL dir = FileLocator.toFileURL(entry);
  httpService.addWebApp(dir, "/");
 }

結果可以在 http://localhost/ 看到 haha.hello.httpdoc bundle 中的 htdoc 目錄。

Refactoring

為了減少重複寫這些註冊 Register 以及依賴 org.eclipse.equinox.common 與 org.springframework.osgi.context.BundleContextAware,將這個責任移到 haha.hello.jetty 去,客端只要負責實體化並注入 HttpService 即可,如下面情形。

<bean id="register" 
    class="haha.hello.jetty.Register" init-method="init" destroy-method="destroy">
    <property name="httpService" ref="httpService" />
    <property name="webappDir" value="htdoc" />
    <property name="contextPath" value="/project" />
</bean>    

觀察

  1. 簡單測試目錄註冊方式與整個 Spring-driven HttpService 的可用性,如果 htdoc 包成 jar 的話,還需要再進一步修改與考量。
  2. spring-driven jetty 也可以支援 osgi-standard http service interface,不過這裡沒有實做。
  3. 目前只有實做簡單的註冊,接下來可以考慮測看看 jsp/servlet/spring-mvc 等機制。
  4. 目前註冊端需要依賴 org.eclipse.equinox.common 與 org.springframework.osgi.context.BundleContextAware [參照 Refactoring 一節,移該責任到 jetty 啟動包去]
  5. 捨棄 OSGi 的 HttpService 代表已偏離標準的約定,這樣一來就需考量與其他 HttpService 實做相容議題。
  6. 大量採用 remix 的做法會形成大者恆大的現象,幾個巨大 bundle 會讓更新下載時間拉長, 只是部份更新卻需要下載數 MB,這點副作用要考慮。

Hello Xfire Web Services and OSGi

0

源起

除了直接接到 spring MVC 來展現網頁之外,對於外部的程式也需要連結,這裡測試 XFire 掛 Spring 的模式來做。

XFire

Chapter 17.5. Web Services

XFire Spring support

XFire JSR 181 Annotations

做法

xfire 有支援 spring ,他將一些預設定義放在 classpath:org/codehaus/xfire/spring/xfire.xml, 不過它是 spring 1.x 版本,即使設定 eclipse buddy 機制取得這個檔,也需要修改,索性直接將這個 檔放到需要的包中。

主要實做下面幾包 bundle 來用。

  1. haha.remix.xfire
  2. haha.hello.ws.userdao
  3. haha.hello.ws.client

haha.remix.xfire

這是個大轉包,沒有加程式,只是改 MANIFEST.MF 來協助。

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.remix.xfire
Bundle-SymbolicName: haha.remix.xfire
Bundle-Version: 1.0.0
Bundle-Activator: haha.remix.xfire.Activator
Bundle-Localization: plugin
Import-Package: javax.servlet;version="2.4.0",
 javax.servlet.http;version="2.4.0",
 org.apache.commons.logging,
 org.osgi.framework;version="1.3.0" 
Bundle-ClassPath: lib/wstx-asl-3.1.0.jar,
 lib/stax-api-1.0.1.jar,
 lib/wsdl4j-1.5.2.jar,
 lib/xfire-all-1.2.2.jar,
 lib/xfire-jsr181-api-1.0-M1.jar,
 .,
 lib/activation-1.1.jar,
 lib/jdom-1.0.jar,
 lib/XmlSchema-1.1.jar
Export-Package: com.ctc.wstx.stax,
 javax.jws,
 javax.xml.stream,
 org.apache.ws.commons.schema,
 org.codehaus.stax2,
........[skip]
Require-Bundle: org.apache.commons.httpclient;resolution:=optional,
 org.apache.commons.codec;resolution:=optional,
 haha.remix.spring;resolution:=optional

這裡需注意 require bundle 都是選擇性的,那是因為有些特定需求要到客端包要求的時候,才會需要, 也不一定會有這些客端包,所以一直掛著讓他一定要這個附屬包也說不過去,所以用選擇性掛上。

haha.hello.ws.userdao

META-INF/spring/bean.xml

<osgi:reference id="httpService" 
  interface="org.osgi.service.http.HttpService"/>

<bean name="servletRegister" 
   class="haha.hello.ws.userdao.ServletRegister" init-method="init">
      <property name="httpService" ref="httpService"/>
</bean>

haha.hello.ws.userdao.ServletRegister 負責啟動 WEB-INF/xx.xml 與註冊服務到 osgi:reference 的 org.osgi.service.http.HttpService

System.setProperty("javax.xml.stream.XMLInputFactory",
  "com.ctc.wstx.stax.WstxInputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory",
  "com.ctc.wstx.stax.WstxOutputFactory");
System.setProperty("javax.xml.stream.XMLEventFactory",
  "com.ctc.wstx.stax.WstxEventFactory");

DispatcherServlet servlet = new DispatcherServlet();
servlet.setNamespace("hello-servlet");

OsgiWebApplicationContext owac = new OsgiWebApplicationContext();
servlet.setContextClass(owac.getClass());
httpService.registerServlet("/services", servlet, null ,null);

WEB-INF/hello-servlet.xml

<import resource="xfire.xml"/>
<osgi:reference id="userDao" 
     interface="haha.hello.jpa.userdao.UserDao"/>

<bean name="helloImpl" class="haha.hello.ws.userdao.HelloImpl">     
   <property name="userDao" ref="userDao"/>
</bean>

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="mappings">
            <value>/hello=wsController</value>
   </property>
</bean>

<bean id="wsController" 
      class="org.codehaus.xfire.spring.remoting.XFireExporter">
        <property name="serviceFactory">
            <ref bean="xfire.annotationServiceFactory"/>
        </property>
        <property name="xfire">
            <ref bean="xfire"/>
        </property>
        <property name="serviceBean">
            <ref bean="helloImpl"/>
        </property>
</bean>

WEB-INF/xfire.xml 取自 xfire classpath:org/codehaus/xfire/spring/xfire.xml 修改一下 singleton 設定而已。

這一包主要將 haha.hello.ws.userdao.Hello 放到 WebServic 去。

haha.hello.ws.userdao.Hello

package haha.hello.ws.userdao;

import javax.jws.WebMethod;
import javax.jws.WebResult;
import javax.jws.WebService;

@WebService(name = "HelloService",  targetNamespace = "http://www.openuri.org/2004/04/HelloWorld")
public interface Hello {
  @WebMethod
  @WebResult
  String getName();
}

haha.hello.ws.userdao.HelloImpl 會注入 osgi:reference 的 haha.hello.jpa.userdao.UserDao 來用。

package haha.hello.ws.userdao;

import haha.hello.jpa.userdao.UserDao;
import javax.jws.WebService;

@WebService(endpointInterface = "haha.hello.ws.userdao.Hello")
public class HelloImpl implements Hello {

 private UserDao userDao;

 private String name = "foo";

 public String getName() {
   return name + userDao.loadUsers().size();
 }
....[SKIP].....

MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.hello.ws.userdao
Bundle-SymbolicName: haha.hello.ws.userdao
Bundle-Version: 1.0.0
Bundle-Activator: haha.hello.ws.userdao.Activator
Bundle-Localization: plugin
Import-Package: com.ctc.wstx.stax,
 javax.jws,
 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
Require-Bundle: haha.remix.spring,
 org.eclipse.equinox.http.jetty,
 haha.hello.jpa.userdao,
 haha.remix.xfire
Export-Package: haha.hello.ws.userdao

啟動後可以直接開瀏覽器到 http://127.0.0.1/services/hello?wsdl 觀察 WSDL

haha.hello.ws.client

陽春型客戶端,只是取用 getName 輸出看看而已,沒有用到 WSDL2JAVA 來轉成 java。

package haha.hello.ws.client;

import java.net.URL;
import org.codehaus.xfire.client.Client;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

 public void start(BundleContext context) throws Exception {
    Client client = new Client(
            new URL("http://127.0.0.1/services/hello?wsdl"));
        Object[] results = client.invoke("getName",null);
        System.out.println(results[0]);
 }
...[SKIP]....

MANIFEST.MF

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: haha.hello.ws.client
Bundle-SymbolicName: haha.hello.ws.client
Bundle-Version: 1.0.0
Bundle-Activator: haha.hello.ws.client.Activator
Bundle-Localization: plugin
Import-Package: org.apache.commons.logging,
 org.codehaus.xfire.client,
 org.osgi.framework;version="1.3.0" 
Bundle-ClassPath: .
Require-Bundle: org.apache.commons.httpclient,
 org.apache.commons.codec,
 haha.hello.ws.userdao,
 haha.remix.xfire

觀察

  1. 越深入企業層面的應用,越發覺既有程式套件之間的相依性很高,之前用一個 classpath 可以找到, 現在轉 OSGi 變成要考慮匯出匯入問題,變的更為複雜,有時現有套件無法滿足,只好直接轉 remix 大包 來實現或是用 eclipse buddy 機制。
  2. 如果使用 import-package 方式,似乎無法保證某些服務會先啟動,於是將 ws.client 中設定 Require-Bundle 來讓某些服務先滿足。

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 無法使用。

Older posts: 1 ... 3 4 5 6 7 ... 18