Tag Archives: Java

servlet-mapping里头的url-pattern

最近在看Servlet 2.4的规范,SRV11 Mapping Requests to Servlets里头讲怎么把HTTP 请求映射给Servlet,主要依据的是web.xml里头的url-pattern。原文其实也很短,就两页,就不摘录了,有兴趣的可以去看原文。网上也有中文版的,但是翻译得很一般,还是直接看英文的好。

看得时候可以略微倒过来,先看语法:

  • / 开头 /* 结尾的是路径匹配。
  • *.开头的是扩展名匹配。
  • /代表默认的servlet。
  • 其他的都被用于完全匹配。

匹配的规则按优先级:

  • 先完全匹配
  • 再进行路径匹配,选择路径最长的servlet
  • 再进行扩展名匹配
  • 如果前面都没有匹配上,而且有默认的servlet的话,则交给默认的servlet处理

这里头我觉得有两个地方需要注意:

1、默认的servlet的url-pattern是 /,而不是/*。除非是为了拦截请求等特殊需要,不要把某一个servlet的url-pattern写成 /*。/*属于路径匹配,而且优先级较高,他会匹配走所有的请求。

2、HttpServletRequest接口里有一个getPathInfo方法,API描述有点拗口,其实返回的就是URI里头去掉url-pattern匹配上的那部分之后的内容。比如你有一个处理用户信息的servlet,url-pattern是/user/*,那么当用户访问/user/list时,getPathInfo返回的就是/list。这个在编程是其实也挺管用的。比如对于用户信息,可能会有增删改查等多种操作,你又不想每一种操作都做成一个servlet,这时候你其实只需要user一个servlet,处理的时候只要根据PathInfo就能知道该进行什么样的操作。

用SAX处理xml文件里的multiRef标签

开发某运营商的WebService订购关系通知接口,原先由VAC直接推送,现在中间加了一个网关,VAC推送网关,网关推送给我们,结果解析网关给过来的XML是出现了问题,一抓包发现是请求里头多了一些MultiRef的标签,不符合原先的WSDL定义。比如说userIdType,按正常来说应该是:

<userIdType type="soapenc:int">1</userIdType>

但是实际拿到的却是:

<userIdType href="#id1"/>

指向的是一个id1的标签,需要从下文里头去找出具体的值:

<multiRef id="id1" soapenc:root="0" soapenv:encodingStyle=" "http://schemas.xmlsoap.org/soap/encoding/">http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="soapenc:int">1</multiRef>

大概搜了一下,说其实是AXIS的一个特有的东西,甚至可以说是一个bug,AXIS2里头都已经取消了multiRef,要在AXIS1里头去掉这个也容易,只要改一下配置就可以了,于是联系网关的人,但是人家说这种int型就是用multiRef来传的,至于让他们改配置,自然不现实,人家说你用AXIS来解析好了。。。没办法,只能自己来弄了。

AXIS肯定不想用,这样的话就得大改。找了一下,网上有方法可以直接把格式转换一下,但是似乎略显复杂,而且接口相对于原来的程序而言也不太合适,于是还是决定自己解析。原先只是根据WSDL生成了请求对象,内容是使用SAX来解析的,大概的代码就是这么几行:

SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
VACReqHander handler = new VACReqHander();
parser.parse(in, handler);

主要就是这个VACReqHander,它继承了org.xml.sax.helpers.DefaultHandle,并且重载了其中的一下方法,这些方法会在适当的时候被parser调用。主要的方法有:

public void startDocument() //开始时被调用
public void startElement(String uri, String localName, String qName, Attributes attributes) //元素开始时被调用
public void characters(char[] ch, int start, int length) //取到值的时候被调用
public void endElement(String uri, String localName, String qName) //元素结束时被调用
public void endDocument() //结束时被调用

原先的实现比较简单,在startElement()里头把key放到一个全局的变量里头,即:key = qName,然后再characters()里头根据key来判断具体是哪个元素并且赋值,比如:

if ("userIdType".equals(key))
req.setUserIdType(Integer.valueOf(data));

但是这种只能处理标准格式的数据,如<key>value</key>,但是类似<userIdType href="#id1"/>这种数据的话characters()根本就没有被调用。大概debug了一下,发现这些都被放在startElement()attributes对象里头。这样就好办了,首先定义几个变量来保存数据:

String userIDtypeHref = null; //userIdType元素里的href对应的值,去掉#号,这里即id1
String multiRefID = null; //multiRef元素里的id对应的值,这里即id1
Hashtable<String,int> multiRefs = new Hashtable<String,Internet>(); //存储multiRef里头id和值的对应关系,这里即id1=1

然后在startElement()里头设置变量:

if(qName.equals("userIdType")){
userIDtypeHref = attributes.getValue("href").substring(1);
}
if(qName.equals("multiRef")){
multiRefID = attributes.getValue("id");
}

然后在 characters() 里取得multiRef里的value,并存储到Hashtable:

if ("multiRef".equals(nodeName)) {
if (refid!=null && isDigit(data))
{
multiRefs.put(multiRefID, Integer.valueOf(data));
}
}

最后在endDocument()设置一下userIdType的值:

if (req.getUserIdType()==0 && userIDtypeHref!=null)
req.setUserIdType(multiRefs.get(userIDtypeHref));

其他int类型的值,比如updateType可以同样处理

程序切割超大日志文件

前阵子做的一个信令统计分析的项目,数据量实在太大了,信令日志入库之前需要根据LAC按地市切割之后分别入库,考虑到性能决定还是用C++写个小程序来切割,平台是Server2003,所以就拿VC6来做,不过对于MFC那些类都不太熟,也懒得去查,所以主程序基本上就是C写的,不过考虑到性能,不能一行一行读,还得有个buffer一次性读过来,然后处理,结果弱爆了,就这么一小程序半天也没搞定,总是有异常,而且错误都不知所云,debug也找不着个所以然来。最后一怒之下写了个java,用的BufferReader/Writer,半个小时就搞定,在笔记本上试了一下,切一个1G的文件用了49秒,也就是说读写都是20多MB/s,考虑的硬盘的速度,提升的空间已经不太大了。

不得不吐槽一下,VC6是我用过最烂的IDE,没有之一。哪天要是能摆脱Visual Studio我就去搞个mac去。

Buffer的大小是1MB,太大了对于速度也没多大影响,白白占用内存而已