在下面的例子中,我们将会创建一个 OSGI 服务,OSGI 服务的创建需要分两个步骤完成:
第一步:创建文件一个接口。
第二步:提供该接口的实现。
下面的例子中,我们创建一个名叫 DictionaryService 的服务,其功能是检查一个单词是否拼写正确,下面是该服务的定义:
/*
* Apache Felix OSGi tutorial.
**/
package tutorial.example2.service;
/**
* 简单的定义了一个字典服务接口,该接口的功能只是简单的验证一个词是否正确。
**/
public interface DictionaryService
{
/**
* Check for the existence of a word.
* @param word the word to be checked.
* @return true if the word is in the dictionary,
* false otherwise.
**/
public boolean checkWord(String word);
}
上面定义的接口服务很简单,我们只需要实现一个方法。需要注意个是我们将该接口保存在 tutorial.example2.service 包下,而将其实现保存在 tutorial.example2 包下,这是因为 DictionaryService 是对外暴露的服务,而其实现类不需要对外暴露,是私有的部分,这样做可以方便我们将需要共享和代码和不需要共享的代码分隔开来,也能够降低接口和实现类之间的耦合度。
下面的代码中我们使用 context 注册了一个 DictionaryService 的实现,如下:
/*
* Apache Felix OSGi tutorial.
**/
package tutorial.example2;
import java.util.Hashtable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceEvent;
import tutorial.example2.service.DictionaryService;
/**
* 此类实现了一个简单的 bundle,并且使用 context 注册了一个英文字典服务到 OSGI 框架。
* 此处英文字典服务只是使用了一个简单的内部类实现。
**/
public class Activator implements BundleActivator
{
/**
* Implements BundleActivator.start(). Registers an
* instance of a dictionary service using the bundle context;
* attaches properties to the service that can be queried
* when performing a service look-up.
* @param context the framework context for the bundle.
**/
public void start(BundleContext context)
{
Hashtable<String, String> props = new Hashtable<String, String>();
props.put("Language", "English");
context.registerService(
DictionaryService.class.getName(), new DictionaryImpl(), props);
}
/**
* Implements BundleActivator.stop(). Does nothing since
* the framework will automatically unregister any registered services.
* @param context the framework context for the bundle.
**/
public void stop(BundleContext context)
{
// NOTE: The service is automatically unregistered.
}
/**
* A private inner class that implements a dictionary service;
* see DictionaryService for details of the service.
**/
private static class DictionaryImpl implements DictionaryService
{
// The set of words contained in the dictionary.
String[] m_dictionary =
{ "welcome", "to", "the", "osgi", "tutorial" };
/**
* Implements DictionaryService.checkWord(). Determines
* if the passed in word is contained in the dictionary.
* @param word the word to be checked.
* @return true if the word is in the dictionary,
* false otherwise.
**/
public boolean checkWord(String word)
{
word = word.toLowerCase();
// This is very inefficient
for (int i = 0; i < m_dictionary.length; i++)
{
if (m_dictionary[i].equals(word))
{
return true;
}
}
return false;
}
}
}
注意,我们并没有在 stop 方法中注销服务注册,因为 OSGI 框架在 bundle 停止的时候会自动完成该操作。上面的 DictionaruService 实现很简单,字典中仅仅包含五个存在数组中的单词,这在实际的应用中是不适用的,但是这并不是我们的目的,我们真正的目的在于使用上面的例子作为教学示例。
接下来我们需要创建一个 manifest.mf 文件用于描述此 bundle 的元数据信息,如下:
Bundle-Name: English dictionary
Bundle-Description: A bundle that registers an English dictionary service
Bundle-Vendor: Apache Felix
Bundle-Version: 1.0.0
Bundle-Activator: tutorial.example2.Activator
Export-Package: tutorial.example2.service
Import-Package: org.osgi.framework
编译、打包源文件:
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo02\src> javac -cp ..\..\..\bin\felix.jar -encoding UTF-8 -d ../target .\tutorial\example2\*.java .\tutorial\example2\service\*.java
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo02\src> cd ../target
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo02\target> cp ..\src\manifest.mf
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo02\target> jar cvfm example2.jar .\manifest.mf -C . .已添加清单正在添加: manifest.mf(输入 = 286) (输出 = 171)(压缩了 40%)
正在添加: tutorial/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: tutorial/example2/(输入 = 0) (输出 = 0)(存储了 0%)正在添加: tutorial/example2/Activator$1.class(输入 = 211) (输出 = 163)(压缩了 22%)
正在添加: tutorial/example2/Activator$DictionaryImpl.class(输入 = 908) (输出 = 557)(压缩了 38%)
正在添加: tutorial/example2/Activator.class(输入 = 1043) (输出 = 554)(压缩了 46%)
正在添加: tutorial/example2/service/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: tutorial/example2/service/DictionaryService.class(输入 = 185) (输出 = 152)(压缩了 17%)
PS D:\devInstall\apache\felix-framework-6.0.0\examples\demo02\target>
安装运行:
g! install file:./examples/demo01/target/example2.jar
java.io.FileNotFoundException: .\examples\demo01\target\example2.jar (系统找不到指定的文件。)
g! lb
START LEVEL 1
ID|State |Level|Name
0|Active | 0|System Bundle (6.0.0)|6.0.0
1|Active | 1|jansi (1.17.1)|1.17.1
2|Active | 1|JLine Bundle (3.7.0)|3.7.0
3|Active | 1|Apache Felix Bundle Repository (2.0.10)|2.0.10
4|Active | 1|Apache Felix Gogo Command (1.0.2)|1.0.2
5|Active | 1|Apache Felix Gogo JLine Shell (1.1.0)|1.1.0
6|Active | 1|Apache Felix Gogo Runtime (1.1.0)|1.1.0
8|Active | 1|Service listener example (1.0.0)|1.0.0
g! install file:./examples/demo02/target/example2.jar
g! lb 11:27:02START LEVEL 1
ID|State |Level|Name
0|Active | 0|System Bundle (6.0.0)|6.0.0
1|Active | 1|jansi (1.17.1)|1.17.1
2|Active | 1|JLine Bundle (3.7.0)|3.7.0
3|Active | 1|Apache Felix Bundle Repository (2.0.10)|2.0.10
4|Active | 1|Apache Felix Gogo Command (1.0.2)|1.0.2
5|Active | 1|Apache Felix Gogo JLine Shell (1.1.0)|1.1.0
6|Active | 1|Apache Felix Gogo Runtime (1.1.0)|1.1.0
8|Active | 1|Service listener example (1.0.0)|1.0.0
12|Resolved | 1|English dictionary (1.0.0)|1.0.0
Bundle ID: 12
g! start 12
Ex1: Service of type tutorial.example2.service.DictionaryService registered. // Service listener example 监听到服务注册。
g! stop 12
Ex1: Service of type tutorial.example2.service.DictionaryService unregistered. // Service listener example 监听到服务注销。
如果 example1 中的 bundle 是正常运行的,则 example2 中的 服务在注册和注销的时候,example1 会接收到相应的服务注册或者注销信息。在 example3 我们将会创建调用字典服务的客户端来使用我们创建的服务。