星期三, 六月 25, 2008

[JAVA]OSGi入门:注册一个服务[译]

欢迎回来EclipseZone OSGi迷你系列。终于,我们准备接触Services了。我认为,Service层是OSGi最精彩的部分,所以接下来的部分将会很有趣。 

上一次我们看了MovieFinder接口的例子,我们讲了将使用MovieLister搜索影片。事实上你可以认为这个例子——它从Martin Fowler著名页上的"依赖注入",也正如所知道的"反转控制"(Inversion of Control)IoC 

重新用IoC来尝试解决这个问题。一个MovieLister并不特别关心原始电影数据的出处,所以我们使用MovieFinder接口来从它隐藏细节。思想是我们可以代替MovieFinder的任一细节,比如获取数据库或者甚至调用一个Amazon Web Service,既MoiveLister仅仅依赖于这个接口,不是任意具体实现。 

到目前为止一切顺利,但是在一些点上,我们必须确实给一个具体的MovieFinderMovieLister的实现。与其让MovieLister到外面去调用一个查找方法,不如我们用一个外部的容器""一个合适的对象给它。因此这个成为反转控制。不少这样的容器已经被开发出来了,例如PicoContainerHiveMindSpring甚至是EJB3.0。可是所有这些容器都有一个限制:它们大多是静态的。一旦一个MovieFinder给予了一个MovieLister,它就相当于JVM的生命期。 

OSGi也允许我们实现IoC模式,但是在动态方法中。它可能动态的提供MovieFinder的实现给MovieLister并在之后移除它们。接下来,我们能够做到在一个文本文件查找电影的应用程序到使用Amazon Web Services的查找它们的热交换。 

这就是服务层帮我们做的。十分简单,我们注册一个MovieFinder服务到Service Regisgry中。之后MovieLister可以用那个MovieFinder服务被提供出来。一个服务因此不比一个Java对象——POJO多些什么——并且它被注册在Java接口的名称下面(一个POJI?)。 

这次,我们将这是看看使用registry来注册服务。稍后,我们看看如何获得这个registry外的服务并提供一个MovieLister 

我们将添加上次我们建立的BasicMovieFinderBundle。我们不需要修改已存在的类,我们仅仅需要添加一个Bundle的激活器。所以复制以下代码到osgitut/movies/impl/BasicMovieFinderActivator.java文件中: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package osgitut.movies.impl;

 

import org.osgi.framework.*;

 

import osgitut.movies.*;
import java.util.Properties;
import java.util.Dictionary;

 

public class BasicMovieFinderActivator implements BundleActivator {
    private ServiceRegistration registration;

 

    public void start(BundleContext context) {
        MovieFinder finder = new BasicMovieFinderImpl();

 

        Dictionary props = new Properties();
        props.put("category", "misc");

 

        registration = context.registerService(
                               MovieFinder.class.getName(),
                               finder, props);
    }

 

    public void stop(BundleContext context) {
        registration.unregister();
    }
}



现在替换BasicMovieFinder.mf的内容: 
1
2
3
4
5
6
7
8
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Basic Movie Finder
Bundle-SymbolicName: BasicMovieFinder
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.BasicMovieFinderActivator
Import-Package: org.osgi.framework,
 osgitut.movies;version="[1.0.0,2.0.0)"


自从上一次以来,有两个信息被添加到这个manifest。第一个是Bundle-Activator行,它告诉框架关于我们的Bundle的新激活器——我们上次没有用到它。同样,我添加了org.osgi.framework到这个导入到包。正如我们的Bundle的上版本不能同框架交互,它不需要导入这个OSGi API包。 

现在你可以重新建立BasicMovieFinder.jar文件: 
1
2
> javac -classpath equinox.jar:MoviesInterface.jar osgitut/movies/impl/*.java
> jar cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class


回到OSGi控制台,你将看到上次的BasicMovieFinder.jar仍然被安装着。那么你只需要通过输入update N来告诉OSGi去更新这个Bundle,这个NBundle的数字标识(你用ss命令找到的那个)。现在使用start N来启动Bundle并且你会看见……很小的事情发生了。 

事实上,我们仅仅用OSGi Service Registry注册了我们第一个服务,但是可惜的是没有人在"另一端",所以这个注册不能产生任何可视化的影响。如果我们想要亲自保证我们的代码实际做了些什么,我们将继续挖掘,并且我们可以使用以下命令: 
1
services (objectClass=*MovieFinder)


我们将看到以下输出: 
1
2
3
{osgitut.movies.MovieFinder}={category=misc, service.id=22}
  Registered by bundle: file:BasicMovieFinder.jar [4]
  No bundles using service.


很好,我们的服务被注册了,并且我很想继续告诉你如何查找服务并在其他的Bundle中使用它,但是那将在另外一天了。在这期间,看看你可以用services命令做些什么。开始时常是输入没有任何表达式的services,之后——那确实可以通过服务的过滤仅仅显示器中的一个。不使用过滤,你将看到所有的注册了的服务。有着令人惊讶的巨大数字。 


参考

[JAVA]OSGi入门:Bundle之间的依赖[译]

在我们上个指导部分,我们看了Bundle如何能启动和停止,和它们如何能与框架交互和每一个生命周期。那么Bundle真正能做什么呢? 

Bundles是一个模块。它们允许我们分割我们完整的项目为能够被单独载入到OSGi运行时的可管理的片段。问题是,是否我们喜欢与否,模块总是以来在一些其他的模块上。在原来旧的Jar里,从没有一个可靠的方法来指定一个对其他Jar的依赖(有的,manifest文件中的Class-Path条目不是做这个的可靠方法)。因此,你从不能真正明确是否这个代码在Jar包里正常工作,或者在运行时将抛出ClassNotFoundException异常。 

OSGi非常优美的修正了这个问题。但是展示出来比告诉你更好……那么让我们赶紧获得这些代码。不幸的是我们知道现在还在使用默认的包,但是这不会再继续了;我们将开始使用正式的包来开始工作。所以让我们使用一个非常简单的JavaBean风格的类开始,你要复制以下代码到osgitut/movies/Movie.jar文件中: 
1
2
3
4
5
6
7
8
9
10
11
12
package osgitut.movies;

 

public class Movie {
    private final String title;
    private final String director;

 

    public Movie(String title, String director) {
        this.title = title; this.director = director;
    }
    public String getTitle() { return title; }
    public String getDirector() { return director; }
}



现在我们将在相同的包里创建一个接口。创建文件osgitut/movies/MovieFinder.java,并复制以下代码到里面: 
1
2
3
4
5
package osgitut.movies;

 

public interface MovieFinder {
    Movie[] findAll();
}


现在让我们将这两个类放到一个Bundle中。是的,我们的Bundle将荒谬的小而且几乎没用,但是现在已经OK了。就像之前我们需要创建一个manifest文件一样,打开MoviesInterface.mf,并且拷贝下面的内容到里面: 

1
2
3
4
5
6
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Movies Interface
Bundle-SymbolicName: MoviesInterface
Bundle-Version: 1.0.0
Export-Package: osgitut.movies;version="1.0.0"


在这里有了一个之前我们没见过的新的行:Export-Package。这只是说包osgitut.movies从这个Bundle被输出了。这可能第一次看的话有些莫名其妙,因为在旧的Jar包里,所有的东西都被输出了。但是你没曾想在你的Jar中能看见的内部中放入一些代码?当然,你能建立一些privateprotected的类,但是它们在你的Jar中都不可见。所以OSGi有效的引入了一个新的代码保护级别:如果包在你的Bundle中没有在Export-Package列表中出现,那么它只能在你的模块内部被访问到。 

你也可能注意到了我们在包的后面附着了个版本号。这个很重要我们稍后将见到。它不是绝对需要提供一个版本号的,顺便说一句,如果你没做,OSGi将自动的分配版本号"0.0.0"给你的包。我认为总是明确的添加一个版本号是很好的策略。 

现在我们来建立这个Bundle 
1
2
> javac osgitut/movies/Movie.java osgitut/movies/MovieFinder.java
> jar -cfm MoviesInterface.jar MoviesInterface.mf osgitut/movies/*.class


我们先不把这个Bundle安装到运行时。首先,我们将建立依赖于它的另一个Bundle。我们要创建一个具体的MovieFinder接口的实现,那么复制以下代码到osgitut/movies/impl/BasicMovieFinderImpl.java文件中: 
1
2
3
4
5
6
7
8
9
10
11
12
package osgitut.movies.impl;

 

import osgitut.movies.*;

 

public class BasicMovieFinderImpl implements MovieFinder {
  private static final Movie[] MOVIES = new Movie[] {
    new Movie("The Godfather", "Francis Ford Coppola"),
    new Movie("Spirited Away", "Hayao Miyazaki")
  };

 

  public Movie[] findAll() { return MOVIES; }
}



现在我们需要一个manifest文件,那么创建BasicMovieFinder.mf 
1
2
3
4
5
6
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Basic Movie Finder
Bundle-SymbolicName: BasicMovieFinder
Bundle-Version: 1.0.0
Import-Package: osgitut.movies;version="[1.0.0,2.0.0)"


注意了我们导入了从另一个包输出的osgitut.movies包。我们也在此时添加了导入版本号的范围。框架在运行期使用范围来匹配导入一个适合的输出。OSGi为版本范围使用了一个语法,与将会与绝大多数的数学方式一样熟悉:方括号意味着包括含(inclusive),圆括号意味着排除(exclusive)。我们将有效的指定版本"1.x" 

再说一下,在import上添加一个版本约束在这个例子中不是必需的,这仅仅是采取一个好的习惯。 

现在我们来编译和建立我们今天第二个Bundle 
1
2
> javac -classpath MoviesInterface.jar osgitut/movies/impl/BasicMovieFinderImpl.java 
> jar -cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class


最终我们准备在这个Equinox里试试这些Bundle。我这次将不给全面的介绍了,我认你应该学会它。首先安装BasicMovieFinder这个Bundle,然后运行ss。你将发现这个Bundle处于INSTALLED状态: 
1
2
3
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0


你的Bundle列表看起来可能会与我的有些不同,详细点说Bundle ID将取决于你在上次安装和卸载了HelloWorld多少次。以下你需要从思想上转换Bundle ID)。 

INSTALLED仅仅意味着框架得到了这个Bundle,但还没有分析它的依赖。一个方法,使用refresh命令强制Equinox来分析我们的Bundle。那么输入refresh 4然后运行ss,你将看到如下: 
1
2
3
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0


Bundle还是没有被分析!当然,我们需要安装包含Movie类和MovieFinder接口的"接口"Bundle。要确定是这个问题,输入 diag 4来获得诊断信息: 
1
2
file:BasicMovieFinder.jar [4]
  Missing imported package osgitut.movies_[1.0.0,2.0.0).


没错,这就是问题所在:我们没有导入osgitut.movies包,因为没有Bundle输出它。那么现在安装MovieInterface.jarBundle,并运行ss,列表将看起来如下: 
1
2
3
4
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0
5       INSTALLED   MoviesInterface_1.0.0


最后的步骤是让Equinox再次尝试分析BasicMovieFinderBundle,运行refresh 4ss命令将输出如下: 
1
2
3
4
id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       RESOLVED    BasicMovieFinder_1.0.0
5       RESOLVED    MoviesInterface_1.0.0


BasicMovieFinderBundle现在被RESOLVED了!这是一个必须的步骤,因为直到Bundle处于RESOLVED前它都不能启动,并且它不能提供依赖给其他的Bundle 

注意,通常像这样手工分析不是必需的。通常Bundle将在它们被需要的时候自动被分析——例如,注意MovieInterfaceBundle现在是RESOLVED,尽管我们没有明确的refresh它。 

暂且到这里。如果对使用Equinox Console做的事情感兴趣,那么看看IBM开发网络上的Chirs Aniszczyk的精彩文章。为下个部分保持状态,我们将开始进入到OSGi Services的研究中。 

[JAVA]OSGi入门:与框架交互[译]

欢迎回来EclipseZone OSGi指导系列。 

上一次,我们看了一个简单的Hello WorldBundle在开始和结束的时候输出一段信息。它通过实现BundleActivator接口和startstop方法。现在再看看代码,在特定方法startstop方法的标记中,你将注意到我们传递了一个参数BundleContext。在指导的这个部分中,我们将研究下BundleContext并且我们能用它来做什么。 

BundleContext是一个OSGi框架传递给我们的Bundle的一个魔力入场券。当代码需要在任何时候与框架交互时,你将用到BundleContext。事实上这是用OSGi API交互的唯一方法,并且框架在Bundle启动的时候将这些入场券发通过它们的BundleActivator发给每一个Bundle 

如果你从上次课程一直有个Equinox在运行,你不需要重启它。如果它没有运行,那么记住启动它的命令: 
1
> java -jar equinox.jar –console


在提示符后输入ss并且你将看到上次课程的Hello World Bundle一直处于installed。甚至在你关闭并重启Equinox之后,因为OSGi框架还残留着运行之间它的状态。 

对于这次练习,我们将写一个Bundle搜索出并卸载Hello World。我们可以从控制台简单的使用usinstall命令,但是我们想见识下它如何使用OSGi API程序化的完成。所以,我们创建一个新的名为HelloWorldKiller.jar文件并复制如下代码: 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import org.osgi.framework.*;

 

public class HelloWorldKiller implements BundleActivator {
    public void start(BundleContext context) {
        System.out.println("HelloWorldKiller searching...");
        Bundle[] bundles = context.getBundles();
        for(int i=0; i<bundles.length; i++) {
            if("HelloWorld".equals(bundles[i]
                                   .getSymbolicName())) {
                try {
                    System.out.println("Hello World found, "
                                     + "destroying!");
                    bundles[i].uninstall();
                    return;
                } catch (BundleException e) {
                    System.err.println("Failed: "
                                     + e.getMessage());
                }
            }
        }
        System.out.println("Hello World bundle not found");
    }

 

    public void stop(BundleContext context) {
        System.out.println("HelloWorldKiller shutting down");
    }
}


现在创建manifest。再说下,记住结尾的空行是非常重要的。复制以下内容到HelloWorldKiller.mf 
1
2
3
4
5
6
7
Manifest-Version: 1.0
Bundle-Name: HelloWorldKiller
Bundle-Activator: HelloWorldKiller
Bundle-SymbolicName: HelloWorldKiller
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework

 



现在,编译并建立Jar 
1
2
> javac -classpath equinox.jar HelloWorldKiller.java
> jar -cfm HelloWorldKiller.jar HelloWorldKiller.mf HelloWorldKiller.class


回到OSGi控制台,使用install file:HelloWorldKiller.jar安装新的Bundle,然后输入ss。状态列表现在看起来会像这样: 
1
2
3
4
id      State       Bundle
0       ACTIVE      system.bundle_3.2.1.R32x_v20060919
1       ACTIVE      HelloWorld_1.0.0
2       INSTALLED   HelloWorldKiller_1.0.0


让我们通过输入start 2运行Hello World Killer。你将看到如下输出: 
1
2
3
HelloWorldKiller searching...
Hello World found, destroying!
Goodbye EclipseZone Readers!

注意,最后一行输出来自于我们原始的Hello World Bundle。因为我们运行Killer之前它是active状态,它要变成uninstalled前变为stoped,由于它的BundleActivatorstop方法运行了。 

再看下ss的输出结果,Hello World消失了。 
1
2
3
id      State       Bundle
0       ACTIVE      system.bundle_3.2.1.R32x_v20060919
2       ACTIVE      HelloWorldKiller_1.0.0

你可能惊叹这是一个安全问题。出现了任何一个Bundle可以卸载任何其它的Bundle!幸运的是OSGi有一个全面的安全层给予所有框架交互的细粒度控制,例如你可能限制卸载一个指定的"管理"Bundle的权限。然而,获得安全工作大部分是一个配置问题,并且在这个系列中,我们将聚焦于这些代码。 

这就是这部分的内容了。在下次前,为什么不看看BundleContext接口和看看你还能使用它来做些什么?例如,你可以试着使用installBundle方法来程序化的装载一个新的Bundle。或者你可以获得所有当前安装的Bundle的列表并输出它们最后一次修改的日期和时间。为了帮助你入门,查看Javadocs for the OSGi Release 4 APIs 


参考

[JAVA]OSGI入门:你的第一个Bundle

再过一到两周,EclipseZone将运行一系列OSGI的简短公告。放到一起它们将从一个平滑的道路进入掌握OSGi编程的艺术,不过每个公告只有一个新技术,并且它尽可能的通过10分钟来完成。所以,我们想要展示OSGi开发能够如何简单,因此我们将不使用Eclipse来开发——仅仅使用一个文本编辑器和基本的命令行工具来做。那么,欢迎来到“OSGi入门”系列。

事实上,第一个公告将比其他的要稍微长一点,因为我们需要设置一个非常基本的工作环境。开始之前,我们需要一个OSGi框架来运行。有三种开源实现来选择:Apache FelixKnopflerfishEquinox。我们将要写的代码都会统一在你选择的任意一个上都没问题,但是在启动指令上将有小小的不同。在EclipseZone中我们将使用Equinox,这个runtime是Eclipse自己创建的。你能从你已存在的Eclipse安装程序中复制一个副本:只需要找到org.eclipse.osgi_3.2.1.R32x_v20060919.jar文件,并复制它到一个空的目录(这个版本字符串可能不同,取决于你的Eclipse版本)。如果你没有一个Eclipse的版本,那么你可以进下载这个Jar文件:http://download.eclipse.org/eclipse/equinox/

要保持命令的简短,我们重命名这个Jar文件为equinox.jar。现在把命令提示符带到我们的开发目录下并运行以下命令:
1
> java -jar equinox.jar –console


几秒钟,osgi>提示符将显示出来。恭喜,你正在运行OSGi!

osgi>提示符给我们访问Equinox中的命令来控制框架。如果你愿意,键入help来查看命令列表,并玩玩它们。那就完了?现在输入ss。这个是最频繁使用的命令;它代表“short status”并且它向我们展示已经安装了的Bundle列表,和它们当前的状态。(一个“Bundle”在OSGi术语中是一个模块。或者如果你是一个Eclipse开发人员,你可以认为它们像plug-ins一样;Bundles和plug-ins根本上一样的东西。)

Equinox将输出如下语句:
1
2
3
4
Framework is launched.

id State Bundle
0 ACTIVE system.bundle_3.2.1.R32x_v20060919


这告诉我们有一个Bundle被安装和激活了,并且它是系统Bundle。这是一个在OSGi中总是存在的特殊Bundle,它表示框架本身。

现在我们将要书写我们自己的Bundle。在之前的同样的目录下,建立一个名为HelloActivator.java的文件并复制如下代码到里面:
1
2
3
4
5
6
7
8
9
10
11
import org.osgi.framework.*;

public class HelloActivator implements BundleActivator {
public void start(BundleContext context) {
System.out.println("Hello EclipseZone Readers!");
}

public void stop(BundleContext context) {
System.out.println("Goodbye EclipseZone Readers!");
}
}


一个Bundle也需要一个manifest文件来声明各种关于Bundle的描述数据,例如它的名称,版本等等。所以创建一个名为HelloWorld.mf并复制以下文本到里面。非常肯定的是这个文件以一个空行结尾,不然的话jar命令行工具将截去这个文件。
1
2
3
4
5
6
7
Manifest-Version: 1.0
Bundle-Name: HelloWorld
Bundle-Activator: HelloActivator
Bundle-SymbolicName: HelloWorld
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework
 


现在打开一个新的命令提示符(因为我们想要离开OSGi的运行)并使用下列命令建立Jar文件:
1
2
> javac -classpath equinox.jar HelloActivator.java
> jar -cfm HelloWorld.jar HelloWorld.mf HelloActivator.class


回到OSGi控制台,输入install file:HelloWorld.jar。回复将是“Bundle id is 1”。再次输入ss并且你将看到如下输出:
1
2
3
4
5
Framework is launched.

id State Bundle
0 ACTIVE system.bundle_3.2.1.R32x_v20060919
1 INSTALLED HelloWorld_1.0.0


我们的HelloWorldBundle安装上了,但是它仍然没有激活。我们将在之后的Post中学习这些状态的意思,但是现在我们只需要通过输入start 1启动这个Bundle。“1”是第一列中这个Bundle的ID。当你做完这个你将看到“Hello EclipseZone Readers!”字样的信息。重复到你无聊了为止。不要忘了偶尔输入ss来看看Bundle的状态的变化。

这里发生了什么?我们的代码实现了BundleActivator接口,允许框架重要的生命周期时间通知我们。当Bundle启动了,框架调用start方法,并当Bundle停止了,框架调用stop方法。其他的事情将是在manifest文件中的“Bundle-Activator: HelloActivator”这一行来告诉框架我们的Bundle中的哪个类是启动器。正常情况下,我们将给个完全限定的类名,但是我们懒了下使用了默认的包。

我们的第一部分结束了。下次见

参考:
原始文章http://www.eclipsezone.com/eclipse/forums/m92130843.html

星期五, 十二月 29, 2006

[JAVA]数据读取DataInputStream.readLine()的替换方式

根据jdk上说的,DataInputStream.readLine()这个方法已经被舍弃不用了
原因是它不能很好的完成bytes到charaters的转换,所以方法替换如下
BufferedReader reader = new BufferedReader( new InputStreamReader(in) );
然后用 reader.readLine() 来读取文件里的一行