本文中的客户端,对于例3中的例子进行了优化,使得代码拥有能够处理服务动态变化的问题,而且当此 bundle 在多线程环境下操作也是安全的,具体例子如下:
/*
* Apache Felix OSGi tutorial.
**/
package tutorial.example4;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceEvent;
import tutorial.example2.service.DictionaryService;
/**
* 这个 bundle 使用字典服务检测输入的单词是否正确,因为我们在代码中解决了服务的动态可用性问题,
* 所以代码相对于 Example3 是比较复杂。当服务被注销的时候,则相应服务的使用者会自动停止使用该
* 服务,当无服务被注册的时候,则相应的服务会被自动投入使用。
**/
public class Activator implements BundleActivator, ServiceListener
{
// Bundle's context.
private BundleContext m_context = null;
// The service reference being used.
private ServiceReference m_ref = null;
// The service object being used.
private DictionaryService m_dictionary = null;
/**
* Implements BundleActivator.start(). Adds itself
* as a listener for service events, then queries for
* available dictionary services. If any dictionaries are
* found it gets a reference to the first one available and
* then starts its "word checking loop". If no dictionaries
* are found, then it just goes directly into its "word checking
* loop", but it will not be able to check any words until a
* dictionary service arrives; any arriving dictionary service
* will be automatically used by the client if a dictionary is
* not already in use. Once it has dictionary, it reads words
* from standard input and checks for their existence in the
* dictionary that it is using.
* (NOTE: It is very bad practice to use the calling thread
* to perform a lengthy process like this; this is only done
* for the purpose of the tutorial.)
* @param context the framework context for the bundle.
**/
public void start(BundleContext context) throws Exception
{
m_context = context;
// We synchronize while registering the service listener and
// performing our initial dictionary service lookup since we
// don't want to receive service events when looking up the
// dictionary service, if one exists.
synchronized (this)
{
// Listen for events pertaining to dictionary services.
m_context.addServiceListener(this,
"(&(objectClass=" + DictionaryService.class.getName() + ")" +
"(Language=*))");
// Query for any service references matching any language.
ServiceReference[] refs = m_context.getServiceReferences(
DictionaryService.class.getName(), "(Language=*)");
// If we found any dictionary services, then just get
// a reference to the first one so we can use it.
if (refs != null)
{
m_ref = refs[0];
m_dictionary = (DictionaryService) m_context.getService(m_ref);
}
}
try
{
System.out.println("Enter a blank line to exit.");
String word = "";
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// Loop endlessly.
while (true)
{
// Ask the user to enter a word.
System.out.print("Enter word: ");
word = in.readLine();
// If the user entered a blank line, then
// exit the loop.
if (word.length() == 0)
{
break;
}
// If there is no dictionary, then say so.
else if (m_dictionary == null)
{
System.out.println("No dictionary available.");
}
// Otherwise print whether the word is correct or not.
else if (m_dictionary.checkWord(word))
{
System.out.println("Correct.");
}
else
{
System.out.println("Incorrect.");
}
}
} catch (Exception ex) { }
}
/**
* Implements BundleActivator.stop(). Does nothing since
* the framework will automatically unget any used services.
* @param context the framework context for the bundle.
**/
public void stop(BundleContext context)
{
// NOTE: The service is automatically released.
}
/**
* Implements ServiceListener.serviceChanged(). Checks
* to see if the service we are using is leaving or tries to get
* a service if we need one.
* @param event the fired service event.
**/
public synchronized void serviceChanged(ServiceEvent event)
{
String[] objectClass =
(String[]) event.getServiceReference().getProperty("objectClass");
// If a dictionary service was registered, see if we
// need one. If so, get a reference to it.
if (event.getType() == ServiceEvent.REGISTERED)
{
if (m_ref == null)
{
// Get a reference to the service object.
m_ref = event.getServiceReference();
m_dictionary = (DictionaryService) m_context.getService(m_ref);
}
}
// If a dictionary service was unregistered, see if it
// was the one we were using. If so, unget the service
// and try to query to get another one.
else if (event.getType() == ServiceEvent.UNREGISTERING)
{
if (event.getServiceReference() == m_ref)
{
// Unget service object and null references.
m_context.ungetService(m_ref);
m_ref = null;
m_dictionary = null;
// Query to see if we can get another service.
ServiceReference[] refs = null;
try
{
refs = m_context.getServiceReferences(
DictionaryService.class.getName(), "(Language=*)");
}
catch (InvalidSyntaxException ex)
{
// This will never happen.
}
if (refs != null)
{
// Get a reference to the first service object.
m_ref = refs[0];
m_dictionary = (DictionaryService) m_context.getService(m_ref);
}
}
}
}
}
构建 manifest.mf:
Bundle-Name: Dynamic dictionary client
Bundle-Description: A bundle that uses the dictionary service whenever it becomes available
Bundle-Vendor: Apache Felix
Bundle-Version: 1.0.0
Bundle-Activator: tutorial.example4.Activator
Import-Package: org.osgi.framework,
tutorial.example2.service
编译、打包:
D:\devInstall\apache\felix-framework-6.0.0\examples\demo04\src> javac -cp ..\..\demo02\target\example2.jar;..\..\..\bin\felix.jar -encoding UTF-8 -d ../target tutorial\example4\Activator.java
D:\devInstall\apache\felix-framework-6.0.0\examples\demo04\src> cd ../target
D:\devInstall\apache\felix-framework-6.0.0\examples\demo04\src> cp ../src/manifest.mf ./
D:\devInstall\apache\felix-framework-6.0.0\examples\demo04\src>
D:\devInstall\apache\felix-framework-6.0.0\examples\demo04\target> jar cvfm example4.jar .\manifest.mf -C . .
已添加清单
正在添加: manifest.mf(输入 = 285) (输出 = 182)(压缩了 36%)
正在添加: tutorial/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: tutorial/example4/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: tutorial/example4/Activator.class(输入 = 3147) (输出 = 1592)(压缩了 49%)
安装、运行:
g! install file:./examples/demo04/target/example4.jar 14:10:40
Bundle ID: 14
g! start 14 14:10:42
Enter a blank line to exit.
Enter word: ~ g! lb 14:10:47
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
7|Active | 1|Service listener example (1.0.0)|1.0.0
8|Active | 1|English dictionary (1.0.0)|1.0.0
10|Active | 1|French dictionary (1.0.0)|1.0.0
11|Resolved | 1|Dictionary client (1.0.0)|1.0.0
14|Active | 1|Dictionary client (1.0.0)|1.0.0
g!
上面的处理对于在单线程的应用程序而言,许多操作是多余的,但是对于多线程应用程序,如 GUI 应用,上面的处理是特别有用的。