HttpServiceとWAB(Web Application Bundle)
GlassfishでWebアプリケーションからOSGiバンドルを利用する
前回のサンプルでは、Webアプリケーション自体はwarとしてディプロイし、依存するサービスをOSGiバンドルとしてディプロイして@Resourceでインジェクションしていました。今回は、アプリケーション自体をOSGiバンドルとしてディプロイする方法として以下を試してみます。
- HttpService
- Web Application Bundle(WAB)
HttpService
HttpServiceは、OSGi環境でサーブレットを利用するための仕組みです。特定のコンテキストパスにサーブレットや静的リソースを紐付けて登録します。ただし、Servlet2.2までしか対応していないためFilterやJSPが使用できません。Felixの個別実装のようですが、Filterも使えるExtHttpServiceというのもあります。
GlassfishでHttpServiceを使用する場合には、まず、こちらからHttpServiceを実装したバンドルをダウンロードしてディプロイしておきます。
次にBundleActivatorにて、HttpService#registerServletでサーブレットを登録します。以下のサンプルでは、/helloにHelloServletを登録し、/imagesに/com/azuki3/osgi/hello/httpservice/imagesパッケージにあるリソースを登録しています。
public class HelloActivator implements BundleActivator { public void start(BundleContext context) throws Exception { ServiceReference sRef = context.getServiceReference(HttpService.class.getName()); if (sRef != null) { HttpService service = (HttpService) context.getService(sRef); HelloServlet servlet = new HelloServlet(); service.registerServlet("/hello", servlet, null, null); service.registerResources("/images", "/com/azuki3/osgi/hello/httpservice/images", null); } } // .... }
バンドルを作成してディプロイすると、http://localhost:8080/osgi/helloでサーブレットにアクセスできます(最初のosgiというパスはHttpServiceを使用する場合の固定値のようです)。実際には、HttpServiceなど利用するサービスがActiveになった時点でサーブレットを登録し、解除する処理も必要になります。
Web Application Bundle(WAB)
WABは、OSGi 4.2で追加されたエンタープライズ向けの仕様で、warファイルのマニフェストにOSGiバンドルの情報とコンテキストパス等を追加することで、Webアプリケーションとして直接、OSGiコンテナにディプロイする方法です。
WABに含まれるサーブレットから、前回のサンプルで作成したHelloServiceを利用してみます。まず、NetBeansでMaven Webアプリケーションプロジェクトを作成します。作成したプロジェクトのpom.xmlにOSGi関連の記述を追加します。
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.azuki3</groupId> <artifactId>WABHelloClient</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>WABHelloClient Java EE 6 Webapp</name> <dependencies> <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.core</artifactId> <version>4.2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>MavenHelloServiceAPI</artifactId> <version>${project.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>osgi-cdi-api</artifactId> <version>3.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>6.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.0.1</version> <extensions>true</extensions> <executions> <execution> <id>bundle-manifest</id> <phase>process-classes</phase> <goals> <goal>manifest</goal> </goals> </execution> </executions> <configuration> <manifestLocation>${project.build.directory}/META-INF</manifestLocation> <supportedProjectTypes> <supportedProjectType>bundle</supportedProjectType> <supportedProjectType>war</supportedProjectType> </supportedProjectTypes> <instructions> <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName> <Web-ContextPath>hello</Web-ContextPath> </instructions> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.1-alpha-2</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> <attachClasses>true</attachClasses> <archive> <manifestFile>${project.build.directory}/META-INF/MANIFEST.MF</manifestFile> <manifestEntries> <Bundle-ClassPath>WEB-INF/classes</Bundle-ClassPath> </manifestEntries> </archive> </configuration> </plugin> </plugins> <finalName>WABHelloClient</finalName> </build> </project>
pom.xmlのポイントは以下のとおりです。
- packagingはwarにする
- Declarative Serviceをインジェクションするためのアノテーションを含むosgi-cdi-apiをdependencyに追加する
- maven-bundle-pluginの設定のWeb-ContextPathでコンテキストパスを指定する
Web-ContextPathの値はMANIFEST.MFに反映されディプロイ時に使用されます。
サーブレットは以下のようになります。HelloServiceに@Injectと@OSGiServiceというアノテーションを付加しています。これで別のOSGiバンドルのDSがインジェクションされます。
@WebServlet(name="HelloClient", urlPatterns={"/HelloClient"}) public class HelloClientServlet extends HttpServlet { @Inject @OSGiService(dynamic=true) HelloService helloService; /** * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods. * @param request servlet request * @param response servlet response * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { out.println("<h1>Servlet HelloClient at " + request.getContextPath() + "</h1>"); out.println(helloService.sayHello("Duke")); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } }
バンドルを作成してディプロイすると、http://localhost:8080/hello/HelloClientでサーブレットにアクセスできます。
まとめ
HttpServiceは、Javaのコードからサーブレットやリソースを登録していくのがおもしろいですね。提供される機能はかなりシンプルなものですが、ApacheのWeb ConsoleもHttpServiceの上に構築されているようなので、用途によっては使えそうです。例えばクライアントがFull AjaxのWebアプリケーションで、サーバサイドはJAX-RS(Jersey)のみでよいようなケースなど。
一方、WABはwarとほぼ同じイメージで使えるので、Spring MVCなどの現在よく使われるWebフレームワークを使用するのであれば、こっちを使うことになると思います。その上で、アプリケーションに含まれるサービスやコンポーネントを上手く別バンドルに切り出して、バンドル毎に開発していくのが、OSGiを利用した開発のイメージということになるのでしょうか。今後はもう少し実践的なアプリケーションを作ってみたいと思います。
今回のコードは以下にあります。
kenichiro22 / glassfish-osgi-sample / overview – Bitbucket