■OSGi for Beginners翻訳 第1回(http://d.hatena.ne.jp/iad_otomamay/20080513/p1)
■OSGi for Beginners翻訳 第2回(http://d.hatena.ne.jp/iad_otomamay/20080514/p1)
A Simple Repository to put into a Bundle(バンドルの中に設置するための簡単なリポジトリ)
So let's create a bundle. I'd like a way to connect to a repository of information.
My initial interface should look something like this:
では、バンドルを作ってみましょう。
情報のリポジトリに接続する方法を説明したいと思います。
まずはじめのインターフェイスは、以下のようなものになります。
package repository; public interface RepositoryService { Node put(String path, Node content); Node get(String path); }
It's not much, but it's something to get started: I can use this to put information in, or get information out. My Node class is a simple POJO, which looks something like this:
このインターフェイスは大きくありませんが、これは始まりです。
これを使って、情報をこの内部に保持することや取り出すことができます。
このMyNodeクラスは、以下のようなシンプルなPOJOです。
package repository; import java.util.ArrayList; import java.util.Date; import java.util.List; public class Node { Date created = new Date(); String source = "unknown"; String author = "unknown"; List contents = new ArrayList<String>(); String path; @Override public String toString() { String s = "node: path=" + getPath() + ", author=" + getAuthor() + ", created=" + getCreated() + ", source=" + getSource() + ", data=["; String separator = ""; for (String d : getContents()) { s += separator + d; separator = ","; } s += "]"; return s; } public Node() { } public Node(String content) { getContents().add(content); } public Node(String content, String context) { this(content); setSource(context); } // .. accessors and mutators go here. }
This is a service that can be implemented and tested independently from OSGi; we have nothing that has anything to do with OSGi yet. With that, we probably should go ahead and have an implementation. I originally coded this as a Map:
これは実行可能なサービスであり、OSGiからは独立しています。
OSGiで実行するための仕掛けは、まだ何もありません。
そうして、たぶんさらに実装をするめ無くてはならないでしょう。
もともと、私はMapとして以下のコードを記述しました。
import java.util.HashMap; import java.util.Map; import repository.Node; import repository.RepositoryService; public class MapRepositoryService implements RepositoryService { Map<String, Node> data=new HashMap<String, Node>(); public Node put(String path, Node content) { return data.put(path, content); } public Node get(String path) { return data.get(path); } }
However, a Map provides poor search facilities, and any hierarchy of information is accidental. I'd like to use a DOM for this, instead. So I'll introduce a dependency on XOM for now, and add a new repository service implementation:
しかしながらMapは貧弱な検索機能しか提供しておらず、
しかも情報の階層は時々、思いがけない状態になります。
私は、代わりにDOMをを使いたいと思いました。
そこで、次に私はXOMの依存性を導入しようと思います。
では、新しいリポジトリサービスの実装を追加します。
package repository.impl; import java.util.Date; import nu.xom.Attribute; import nu.xom.Document; import nu.xom.Element; import nu.xom.Elements; import nu.xom.Nodes; import nu.xom.ParentNode; import repository.Node; import repository.RepositoryService; public class XMLRepositoryService implements RepositoryService { Document document; Element data; public XMLRepositoryService() { data = new Element("data"); document = new Document(data); } Node toNode(nu.xom.Element node) { if (node.getAttribute("source") == null) { return null; } Node n = new Node(); n.setAuthor(node.getAttributeValue("author")); n.setCreated(new Date(node.getAttributeValue("created"))); n.setSource(node.getAttributeValue("source")); Elements e = node.getChildElements("contents"); n.setPath(node.getAttributeValue("path")); for (int i = 0; i < e.size(); i++) { Element elt = e.get(i); for (int i1 = 0; i1 < elt.getChildCount(); i1++) { n.getContents().add(elt.getChild(i1).getValue()); } } return n; } nu.xom.Element getElement(String path) { while (!path.startsWith("//")) { path = "/" + path; } while (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } Nodes nodes = document.query(path); if (nodes.size() > 0) { return (Element) nodes.get(0); } return null; } public Node get(String path) { Element e = getElement(path); if (e != null) { return toNode(e); } return null; } public Node put(String path, Node content) { Element oldElt = getElement(path); if (oldElt != null) { // need to remove this node! ParentNode p = oldElt.getParent(); p.removeChild(oldElt); } StringBuilder pathBuilder=new StringBuilder("/"); String[] tree = path.split("/"); Element node = data; for (String t : tree) { if (t.length() != 0) { Element child = node.getFirstChildElement(t); if (child == null) { //System.err.println("creating new "+t); child = new Element(t); node.appendChild(child); } pathBuilder.append('/'); pathBuilder.append(t); node = child; } } node.addAttribute(new Attribute("created", content.getCreated() .toString())); node.addAttribute(new Attribute("source", content.getSource())); node.addAttribute(new Attribute("author", content.getAuthor())); content.setPath(pathBuilder.toString()); node.addAttribute(new Attribute("path", content.getPath())); Element contents = new Element("contents"); node.appendChild(contents); for (String c : content.getContents()) { Element e = new Element("content"); e.appendChild(c); contents.appendChild(e); } //System.out.println(data.toXML()); return null; } public static void main(String[] args) { XMLRepositoryService s = new XMLRepositoryService(); s.put("/foo/bar/baz", new Node("stuff")); Node n = new Node("bletch"); n.setAuthor("jottinger"); s.put("/foo/bar/bletch", n); System.out.println(s.get("foo/bar/baz/")); System.out.println(s.get("//foo/bar/baz")); System.out.println(s.get("//foo/bar/bletch")); System.out.println(s.get("foo/bar/")); System.out.println(s.get("//*[@author='jottinger']")); } }
It's still far from perfect, but it's a good start. However, we still haven't done anything with respect to OSGi - we only have a slightly usable repository class. Let's look at how OSGi modules are built.
まだまだ完璧とはいえませんが、手始めにはちょうどよいでしょう。
しかしながら、いまだにOSGiの恩恵を受けられるようなことは何もしていません。
ただ単に、若干使えるリポジトリクラスを手に入れただけです。
さて、OSGiモジュールをどうやって組み立てていくのかを見ていきましょう!!
Sidetrack: Building an OSGi bundle with a Dependency(脱線:依存性を使ったOSGiバンドルを組み立てる)
An OSGi module is a .jar file. In this, it follows the standard .jar file specification , except it has a set of specific requirements in its manifest file .
OSGiモジュールは、jarファイルです。
このJarファイルの中は、マニフェストファイルでの特殊な要件を除けば、
あとは基本的なJarファイルの仕様に従っています。
Let's create a simple module, first. This will simple display a message on startup and shutdown, and along the way will include a .jar file as an internal dependency. This jar file will not be visible to any other bundle - it's just a resource for our tutorial bundle. However, this is a fairly simple and common requirement that is poorly documented. (Well, it's poorly documented for me - I looked for a simple example and couldn't find one.) The dependency will be in a jar, baselib.jar, and it will be one class: baselib.BaseService.
さて、まずは簡単なモジュールを作ってみましょう。
これは、スタートアップとシャットダウンのときに
簡単なメッセージを表示するだけのものです。
そしてこの処理は、内部の依存性にJarファイルを含めるやり方に従っています。
このJarファイルは、他のバンドルからは不可視です。
つまりこのチュートリアルのバンドルのためだけのリソースということです。
しかしながら、これは、貧弱なドキュメントで共通的に本来必要な、
完全にシンプルなサンプルです。
(ほんとうに貧弱なドキュメントであり、
シンプルなサンプルを探しましたが、見つけることはできませんでした。)
依存性は、baselib.jarというJarの中の、おそらく
baselib.BaseServiceというクラスにあります。
package baselib; import java.util.logging.Logger; public class BaseService { Logger log=Logger.getLogger(this.getClass().getName()); public void sayHello() { log.info("Hello, world!"); } }
Complex, earth-shattering stuff here, to be sure. What we need to do is build this file into a .jar of its very own, one I'll call baselib.jar.
ここでの驚くほど複雑な必要作業は、
このファイルを独自の.jarに組み込むことです、
そのJarの一つは筆者がbaselib.jarと呼んでいたものです。
Now, an OSGi bundle requires an "activator," a class that manages the lifecycle of the bundle. An activator implements the org.osgi.framework.BundleActivator interface, which means two methods: start(BundleContext) and stop(BundleContext). These lifecycle methods are where a bundle can register services or start processes, but for us, it's far simpler:
さてOSGiバンドルは、バンドルのライフサイクルを管理するクラスである"activator,"を求めます。
activatorは、org.osgi.framework.BundleActivatorインターフェイスを実装します。
このインターフェイスには、以下の2つのメソッドがあります。
- start(BundleContext)
- stop(BundleContext)
これらのライフサイクルメソッドは、バンドルがサービスに登録するか
または、プロセスを開始することができる場所ですが、
しかし、大いにシンプルです。
package tutorial; import baselib.BaseService; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import java.util.logging.Logger; public class TutorialActivator implements BundleActivator { Logger log=Logger.getLogger(this.getClass().getName()); public void start(BundleContext bc) { log.info("started"); new BaseService().sayHello(); } public void stop(BundleContext bc) { log.info("stopped."); } }
We can't just stuff this into a jar file and have it work for us, sadly (Spring-OSGi can help here, but it's far beyond scope for this article.) We need to build a tutorialbundle.jar with a specific set of files and a specific structure. First off, baselib.jar must be in the root directory of our new jar. As well, we need a MANIFEST.MF that includes some OSGi configuration and startup information:
残念ながら、これをJarファイルに、ただ詰め込んだだけでは動かすことはできません。
(Spring-OSGiはそれをサポートしていますが、この記事の対象からは外れるので割愛します)
そのため、特定のファイルを集め、特定の構造に則って、
tutorialbundle.jarを組み立てる必要があります。
まず第一に、baselib.jarを新しいJarのルートディレクトリ配置する必要があります。
そして、OSGiの設定と起動情報を表すMANIFEST.MFが必要です。
(MANIFEST.MFの内容は以下です。)
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: com.theserverside.tutorial.osgi.TutorialBundle Bundle-Version: 1 Bundle-Activator: tutorial.TutorialActivator Import-Package: org.osgi.framework;version="1.3.0" Bundle-ClassPath: .,baselib.jar
This manifest works on the assumption that our jar looks like this:
このマニフェストファイルを以下のJarファイルの場所に配置すると動作します。
$ jar tvf tutorialbundle.jar 0 Thu Apr 17 11:57:14 EDT 2008 META-INF/ 391 Thu Apr 17 11:57:12 EDT 2008 META-INF/MANIFEST.MF 0 Thu Apr 17 11:29:56 EDT 2008 tutorial/ 714 Thu Apr 17 11:51:02 EDT 2008 tutorial/TutorialActivator.class 902 Thu Apr 17 11:15:28 EDT 2008 baselib.jar
The most important things in our manifest are the imported packages (just the OSGi base framework in this case), the activator class name, and the bundle classpath - a comma-separated set of resources in the jar file. If we can duplicate this structure, then we're ready to install this bundle and run it in Equinox.
マニフェストファイルにおいて最も重要なことは、以下です。
この構造を写すことができたら、バンドルをインストールして、Equinoxで動かす準備は完了です。
$ java -jar org.eclipse.osgi_3.3.2.R33x_v20080105.jar -console osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.3.2.R33x_v20080105 osgi> install file:///workspaces/osgi/tutorial/tutorialbundle.jar Bundle id is 4 osgi> start 4 Apr 17, 2008 11:57:29 AM tutorial.TutorialActivator start INFO: started Apr 17, 2008 11:57:29 AM baselib.BaseService sayHello INFO: Hello, world! osgi> stop 4 Apr 17, 2008 1:29:25 PM tutorial.TutorialActivator stop INFO: stopped. osgi>
Now we can see how to build a bundle, and how to deploy it with dependent jars (a requirement, since our repository class above relies on XOM.)
ここでは、バンドルをビルドする方法と依存するJar(XOMリポジトリクラスに必要な要件のJar)に
デプロイする方法が確認できました。