Instant Messaging java(第三章)03

 
3.3xml 解析子系统
XML 解析是 jabber 服务所做的最复杂的任务了,然而,对于 java Coder 们来说,这个任务显然是小儿科,因为我们用完善的 Java Sax 解析库来做这些工作。我们仅仅需要对查找出处理这些的方法。
服务器的 XML 解析类的任务就是将 xml 流信息写入 Packet 对象,存储的 PacketQueue 。我们用 packet packetQueue 类开始我们的 xml 解析过程
 
3.3.1 描述 Jabber
jabber 协议包含了客户端与服务器端交换 xml 碎片的办法。我们把这些 xml 碎片作为一个包来提交。我们把这些信息包装到 java 对象中,他们工作使用起来要比 xml 字符串简单的多。使用 java 对象还可以享受到 java 的类型检测,继承以及多态等特征的好处。
预览 jabber 协议就可以知道它有三个核心包需要操心:
<message></message>
,<presence></presence>
<iq></iq>
另外需要我们注意的是打开的和关闭的流标签以及流错误包:
<stream:stream>
<stream:error></stream:error>
</stream:stream>
支持这些包和标签从根本上说是很简单的。在本章最后,我们的 jabber 服务将能够识别和控制他们。第一步是识别他们的一般特征和封装他们到 java 类。
Jabber 包其实是一个 xml 片段,因此可以把它当成一个 mini xml 文档。在 java 中又很多的方法处理 xml 文档。最流行的依据 w3cdom 标准。大部分的 javaxml 解析库包含标准的支持 w3cdom java 类。在 dom 中, xml 文档时类似于树的结构体。
我们需要的 xml 表示不是如 dom 标准所能胜任的。我们知道我们接受各种各样的 xml 文档和我们指明的 java 对象。另外,不使用 dom 也可使我们的代码避免依附到 dom 库和建立服务器所需的代码。最后,我们的包类必须做比呈现 xml 片段更多的功能。 Packets 类必须能充当如下两个角色:
Pack Store —— packets 类是一个主要的数据储存类。信息能够被标准的 java 方法调用。我们能够存储包到类似 w3cdom 的树形数据结构。
XML writer —— packet 类能够知道到怎么创建自己的 xml 字符串表示方式。这个特征允许其他类从 xml 字符串呈现对象转换成的 packet 对象。
一个单独的 xml 解析类在本章最后将从接受的 xml 字符串转变成 packet 对象。 Packet 类的数据结构反映了 xml 片段的结构。例如,考虑如下 xml 包:
<message to='recipient'>
<subject>hello</subject>
<body>How are you doing?</body>
</message>
我们可以把它们组织到三个包中:
Packet: message (attribute to='recipient)
Packet: subject
String (value "hello")
Packet: body
String (value "How are you doing?");
在这样的数据结构中,我们能够看到一个包有一个元素名,零个或多个属性名值对,零个或多个子节。一个 packet 的子对象是一个字符串或其 packet 对象。另外,每个包有一个相关的命名空间。
作为一个 java 类,我们能够存储 packet 的子节到 java.util.List 类型的 List 中。他的属性在 java.util.Hashtable ,并且其他值是字符串。如果这个包没有父节点,包的 parent 值是 null
这个类包括三个构造器:
The Packet class constructors
public class Packet {
public Packet(String element){
setElement(element);
}
public Packet(String element, String value){
setElement(element);
children.add(value);
}
public Packet(Packet parent,
String element,
String namespace,
Attributes atts){
setElement(element);
setNamespace(namespace);
setParent(parent);
//Copy attributes into hashtable
for (int i = 0; i < atts.getLength(); i++){
attributes.put(atts.getQName(i), atts.getValue(i));
}
}
parent children 成员变来那个负责 packet 的树形结构和他们的子节。我们能够用 LinkedList 类型的类存储 packet Strings 。另外, packet 的命名空间和元素名必须被存储成字符串。
String namespace;
public void setNamespace(String name) { namespace = name; }
public String getNamespace() { return namespace; }
String element;
public void setElement(String element) { this.element = element; }
public String getElement() { return element; }
Packet parent;
public Packet getParent() { return parent; }
public void setParent(Packet parent){
this.parent = parent;
if (parent != null){
parent.children.add(this);
}
}
LinkedList children = new LinkedList();
public LinkedList getChildren() {return children;}
packet 提供了几个构造器。假设我们用个如下的 xml 包:
<item>
ItemValue
<sub-item>sub-item value</sub-item>
<sub-item>another value</sub-item>
</item>
有三个典型的典型任务,其他的类将在 packet 中执行:
l       获取给定元素名的子包。(例如: <sub-item>sub-item value</sub-item> .
l       获取 packet 的第一个字符串值。
l       获取字符串值相关的第一个子包。
第一个方法是从一个给定的元素名定位第一个子包。他在 getFirstChild() 中实现。例如,考虑最近的 <itme>xml 包。你能够在 itme 包对象中调用 getFirstChild(“sub-itme”), 得到元素名为 sub-item 的子包。
public Packet getFirstChild(String subelement){
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object child = childIterator.next();
if (child instanceof Packet){
Packet childPacket = (Packet)child;
if (childPacket.getElement().equals(subelement)) {
return childPacket;
}
}
}
return null;
}
另外一些基本的任务是依据一个元素获得字符串值。例如,我们得到了 <sub-item> 子包,我们能够知道他的值 (“sub-item value”) 。你能够通过调用 <sub-item> 的包对象 getValue ()方法得到。
public String getValue(){
StringBuffer value = new StringBuffer();
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object valueChild = childIterator.next();
if (valueChild instanceof String){
value.append((String)valueChild);
}
}
return value.toString().trim();
}
甚至有许多类似的情况,当我们想知道 sub-packet 的子节的字符串值。再上一个例子中,我们通过调用子包的 getFirstChild(“sub-item”) ,得到 <sub-item> 的子包值。然后调用 getValue ()得到他的字符串值。有了这些方便的方法,我们能够更方便的结合到 getChildValue ()方法中。 public String getChildValue(String subelement){
Packet child = getFirstChild(subelement);
if (child == null){
return null;
}
return child.getValue();
}
输入包经常依赖于会话上下文和其它动作路由。每一个在我们的 jabber 服务中的 client server 能够有一个相关的 session 对象出处 session 上下文。这个 packet 存储这个会话对象的引用。
Session session;
public void setSession(Session session) { this.session = session; }
public Session getSession() {
if (session != null){
return session;
}
if (parent != null){
return parent.getSession();
}
return null;
}
许多 jabber 协议依赖于 packet 属性翻译包相关信息和他的动作。 Packet 类存储属性到 java.util.Hashtable 。另外,他提供了几个方便的方法访问大部分的一般 packet 属性:
l       to ——包的接受对象
l       from ——包发送者
l       id ——包 ID ,唯一标识包
l       type ——包类型。依据协议而定。
Hashtable attributes = new Hashtable();
public String getAttribute(String attribute) {
return (String)attributes.get(attribute);
}
public void setAttribute(String attribute, String value) {
if (value == null){
removeAttribute(attribute);
} else {
attributes.put(attribute,value);
}
}
public void removeAttribute(String attribute){
attributes.remove(attribute);
}
public void clearAttributes(){
attributes.clear();
}
public String getTo() { return (String)attributes.get("to"); }
public void setTo(String recipient) { setAttribute("to",recipient); }
public String getFrom() { return (String)attributes.get("from"); }
public void setFrom(String sender){ setAttribute("from",sender); }
public String getType() { return (String)attributes.get("type"); }
public void setType(String type){ setAttribute("type",type); }
public String getID() { return (String)attributes.get("id"); }
public void setID(String ID) { setAttribute("id",ID); }
}
最后, packet 类能够把它自己写成一个 xml 字符串,依靠 java.io.Writer 。创建 xml 呈现的过程包括遍历树顺序,输出合适的元素,属性,以及子节。
public void writeXML() throws IOException {
writeXML(session.getWriter());
}
public void writeXML(Writer out) throws IOException{
out.write("<");
out.write(element);
//Output the attributes for the element
Enumeration keys = attributes.keys();
while (keys.hasMoreElements()){
String key = (String)keys.nextElement();
out.write(" ");
out.write(key);
out.write("='");
out.write((String)attributes.get(key));
out.write("'");
}
//Empty element
if (children.size() == 0){
out.write("/>");
out.flush();
return;
}
out.write(">");
//Iterate over each child
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object child = childIterator.next();
//Send value to Writer
if (child instanceof String){
out.write((String)child);
//Or recursively write its children's XML
} else {
((Packet)child).writeXML(out);
}
}
out.write("</");
out.write(element);
out.write(">");
out.flush();
}
public String toString(){
try {
StringWriter reply = new StringWriter();
writeXML(reply);
return reply.toString();
} catch (Exception ex){
}
return "<" + element + ">";
}
}
packet 置入,接受来源,一个单独的服务端包队列。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值