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