楼主: jieforest

模块化Java

[复制链接]
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
11#
 楼主| 发表于 2010-1-29 21:11 | 只看该作者
在调用服务处理URL之前,client需要解析服务。我们需要获得一个服务引用,它可以让我们查看服务自身内部的属性,然后利用其来获得我们感兴趣的服务。可是,我们需要能够重复处理相同及不同的URL,以便我们可以把它集成到Equinox或Felix的shell里。实现如下:
package com.infoq.shorten.command;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import com.infoq.shorten.IShorten;

public class ShortenCommand {
        protected BundleContext context;
        public ShortenCommand(BundleContext context) {
                this.context = context;
        }
        protected String shorten(String url) throws IllegalArgumentException, IOException {
                ServiceReference ref =
                        context.getServiceReference(IShorten.class.getName());
                if(ref == null)
                        return null;
                IShorten shorten = (IShorten) context.getService(ref);
                if(shorten == null)
                        return null;
                return shorten.shorten(url);
        }
}

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
12#
 楼主| 发表于 2010-1-29 21:11 | 只看该作者
当shorten方法被调用时,上面这段程序将查找服务引用并获得服务对象。然后我们可以把服务对象赋值给一个IShorten对象,并使用它与前面讲到 的已注册服务进行交互。注意这些都是在同一个VM中发生的;没有远程调用,没有强制异常,没有参数被序列化;只是一个POJO与另一个POJO对话。实际 上,这里与最开始class.forName()例子的唯一区别是:我们如何获得shorten POJO。

为了在Equinox和Felix里面使用这一服务,我们需要放一些样板代码进去。必须提一下,当我们定义manifest时,我们可以在Felix和 Equinox命令行上声明可选依赖,这样,当我们两者中任何一个安装之后,我们就可以运行了。(一个更好的解决方案是将其部署为单独的bundles, 这样我们可以去掉选项;但是如果bundle不存在,activator将会失败,因此无法启动)。Equinox和Felix特定命令的源代码在com.infoq.shorten.command bundle中。

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
13#
 楼主| 发表于 2010-1-29 21:12 | 只看该作者
如果我们安装了命令client bundle,我们将得到一个新命令,shorten,通过OSGi shell可以调用它。要运行该命令,需要先执行java -jar equinox.jar -console -noExit或java -jar bin/felix.jar,然后安装bundle,之后你就可以使用该命令了:
java -jar org.eclipse.osgi_* -console -noExit
osgi> install file:///tmp/com.infoq.shorten-1.0.0.jar
Bundle id is 1
osgi> install file:///tmp/com.infoq.shorten.command-1.0.0.jar
Bundle id is 2
osgi> install file:///tmp/com.infoq.shorten.tinyurl-1.0.0.jar
Bundle id is 3
osgi> install file:///tmp/com.infoq.shorten.trim-1.0.0.jar
Bundle id is 4
osgi> start 1 2 3 4
osgi> shorten http://www.infoq.com
http://tinyurl.com/yr2jrn
osgi> stop 3
osgi> shorten http://www.infoq.com
http://tr.im/Eza8

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
14#
 楼主| 发表于 2010-1-29 21:12 | 只看该作者
注意,在运行时TinyURL和Tr.im服务都是可用的,但是一次只能使用一种服务。可以设置一个服务级别(service ranking), 这是一个整数,取值范围在Integer.MIN_VALUE和Integer.MAX_VALUE之间,当服务最初注册时给 Constants.SERVICE_RANKING赋予相应值。值越大表示级别越高,当需要服务时,会返回最高级别的服务。如果没有服务级别(默认值为 0),或者多个服务的服务级别相同,那么就使用自动分配的Constants.SERVICE_PID,可能是任意顺序的一个服务。

另一个需注意的问题是:当我们停止一个服务时,client会自动失败转移到列表中的下一个服务。每当该命令执行时,它会获取(当前)服务来处理URL压 缩需求。如果在运行期间服务提供程序发生了变化,不会影响命令的使用,只要有此需求时有服务在就成。(如果你停止了所有服务提供程序,服务查找将返回 null,这将会打印出相应的错误信息——好的代码应该确保程序能够预防返回服务引用为null的情况发生。)

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
15#
 楼主| 发表于 2010-1-29 21:12 | 只看该作者
服务跟踪

除过每次查询服务外,还可以用ServiceTracker来代替做这一工作。这就跳过了中间获得ServiceReference的几步,但是要求你在构造之后调用open,以便开始跟踪服务。

对于ServiceReference,可以调用getService()获得服务实例。而waitForService()则在服务不可用时阻塞一段时间(根据指定的timeout。如果timeout为0,则永远阻塞)。我们可以如下重新实现shorten命令:
package com.infoq.shorten.command;

import java.io.IOException;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.infoq.shorten.IShorten;

public class ShortenCommand {
        protected ServiceTracker tracker;
        public ShortenCommand(BundleContext context) {
                this.tracker = new ServiceTracker(context,
                        IShorten.class.getName(),null);
                this.tracker.open();
        }
        protected String shorten(String url) throws IllegalArgumentException,
                        IOException {
                try {
                        IShorten shorten = (IShorten)
                                tracker.waitForService(1000);
                        if (shorten == null)
                                return null;
                        return shorten.shorten(url);
                } catch (InterruptedException e) {
                        return null;
                }
        }
}

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
16#
 楼主| 发表于 2010-1-29 21:13 | 只看该作者
使用Service Tracker的常见问题是在构造后忘记了调用open()。除此之外,还必须在MANIFEST.MF内部引入org.osgi.util.tracker包。

使用ServiceTracker来管理服务依赖通常被认为是管理关系的好方法。在没有使用服务的情况下,查找已输出的服务稍微有点复杂:比 如,ServiceReference在其被解析为一个服务之前突然变得不可用了。存在一个ServiceReference的原因是,相同实例能够在多 个bundle间共享,而且它可以被用来基于某些标准(手工)过滤服务。而且,它还可以使用过滤器来限制可用服务的集合。

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
17#
 楼主| 发表于 2010-1-29 21:13 | 只看该作者
服务属性和过滤器

当一个服务注册时,可以将服务属性一起注册。大多情况下属性可以为null,但是也可以提供OSGi特定或关于URL的通用属性。例如,我们想给服务分级 以便区分优先级。我们可以注册Constants.SERVICE_RANKING(代表优先级的数值),作为最初注册过程的一部分。我们可能还想放一些 client想知道的元数据,比如服务的主页在哪儿,该站点的条款链接。为达此目的,我们需要修改activator:
public class Activator implements BundleActivator {
        public void start(BundleContext context) {
                Hashtable properties = new Hashtable();
                properties.put(Constants.SERVICE_RANKING, 10);
                properties.put(Constants.SERVICE_VENDOR, "http://tr.im");
                properties.put("home.page", "http://tr.im");
                properties.put("FAQ", "http://tr.im/website/faqs");
                context.registerService(IShorten.class.getName(),
                        new Trim(), properties);
        }
...
}

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
18#
 楼主| 发表于 2010-1-29 21:14 | 只看该作者
服务级别自动由ServiceTracker及其他对象来管理,但也可以用特定条件来过滤。Filter是由LDAP风格的过滤器改编而来的,其使用了一种前缀表示法(prefix notation)来 执行多个过滤。虽然多数情况下你想提供类的名字(Constants.OBJECTCLASS),但你也可以对值进行检验(包括限制连续变量的取值范 围)。Filter是通过BundleContext创建的;如果你想跟踪实现了IShorten接口的服务,并且定义一个FAQ,我们可以这样做:
...
public class ShortenCommand
        public ShortenCommand(BundleContext context) {
                Filter filter = context.createFilter("(&" +
                        "(objectClass=com.infoq.shorten.IShorten)" +
                        "(FAQ=*))");
                this.tracker = new ServiceTracker(context,filter,null);
                this.tracker.open();
        }
        ...
}

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
19#
 楼主| 发表于 2010-1-29 21:14 | 只看该作者
在定义服务时可以被过滤或可以设置的标准属性包括:
service.ranking (Constants.SERVICE_RANKING) - 整数,可以区分服务优先级
service.id (Constants.SERVICE_ID) - 整数,在服务被注册时由框架自动设置
service.vendor (Constants.SERVICE_VENDOR) - 字符串,表明服务出自谁手
service.pid (Constants.SERVICE_PID) - 字符串,代表服务的PID(persistent identifier)
service.description (Constants.SERVICE_DESCRIPTION) - 服务的描述
objectClass (Constants.OBJECTCLASS) - 接口列表,服务被注册在哪些接口下

过滤器语法在OSGi核心规范的 3.2.7节 “Filter syntax”中有定义。最基本的,它允许如等于(=)、约等于(~=)、大于等于、小于等于以及子字符串比较等操作符。括号将过流器分组,并且可以结合 使用“&”、“|” 或“!”分别代表and、or和not。属性名不是大小写敏感的,值可能是(如果不用~=作比的话)。“*”是通配符,可用来支持子字符串匹配,比如 com.infoq.*.*。

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
20#
 楼主| 发表于 2010-1-29 21:15 | 只看该作者
总结

本文中,我们介绍了如何使用服务进行bundle间通信,以替代直接类引用的方法。服务可以让模块系统动态化,这样就能应对在运行时服务的变化问题。我们 还接触到了服务级别、属性及过滤器,并使用标准服务跟踪器来更容易的访问服务并跟踪变化的服务。我们将在下一部分介绍如何用声明式服务使得服务的编写更加容易。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表