起因是这样的,与其他公司的系统集成,通信使用 xml-rpc ,xml-rpc 规范参考http://www.xmlrpc.com/spec。
现在有这样一个请求:
<methodCall>
<methodName>GetRtVideoTicket</methodName>
<params>
<param>
<value><string>string</string></value>
</param>
</params>
</methodCall>
也就是说,客户端请求执行一个方法。但是 java 语言中没有独立存在的方法,而apchce的xml-rpc实现中我也没找到响应这种请求的方法,必须先注册一个处理类handler,客户端才能调用这个类对象的方法。上面这个请求中,方法名要这样写才能调用成功:
<methodName>handler.GetRtVideoTicket</methodName>
对方的系统使用的是 .net + ext 实现,好像 c# 跟 c++ 一样,有不依赖于具体类对象的方法(函数)存在。对方的系统已部署运行,且不会更改客户端,我只会java,在有限的时间实现服务端,只能使用自己熟悉的语言,可能的方法就是http请求过来的时候,把它请求的方法名自己做些改造。也就是将客户端请求方法名由“GetRtVideoTicket”改为“handler.GetRtVideoTicket”。
在服务端Servlet,其中的HttpServletRequest对象是不可修改的,它有大量的get方法,确只有区区2个set方法:
httpRequest.setAttribute(name, o);
httpRequest.setCharacterEncoding(env);
这时,可以使用 filter ,在请求到达 Servlet 前将HttpServletRequest对象替换掉。
javaee中提供了一个HttpServletRequest的包装类HttpServletRequestWrapper,我们可以通过它对HttpServletRequest对象做些处理:
public class MyServletRequestWrapper extends HttpServletRequestWrapper {
public MyServletRequestWrapper(HttpServletRequest request) {
super(request);
}
......
}
比如,覆写它的getInputStream() 方法,就可以用我们自己的InputStream对象,替换掉http请求里的InputStream对象:
@Override
public ServletInputStream getInputStream() throws IOException {
String data = getStreamString(super.getInputStream());
//这里只是简单的替换做示例,正确的做法是把<methodName>标签对里的方法名加上"handler."前缀
data = data.replaceAll("GetRtVideoTicket", "handler.GetRtVideoTicket");
InputStream in = getStringStream(data);
return (ServletInputStream) in;
}
上面有字符串和字节流互相转换的方法,如下:
//从字符串获得字节流
private InputStream getStringStream(String data) {
if (!"".equals(data)) {
MyServletInputStream in = new MyServletInputStream(data);
return in;
}
return null;
}
//从字节流获得字符串
private String getStreamString(InputStream in) {
if (in != null) {
try {
BufferedReader tBufferedReader = new BufferedReader(
new InputStreamReader(in));
StringBuffer tStringBuffer = new StringBuffer();
String sTempOneLine = new String("");
while ((sTempOneLine = tBufferedReader.readLine()) != null) {
tStringBuffer.append(sTempOneLine);
}
return tStringBuffer.toString();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
由于http请求对象里的InputStream对象类型是ServletInputStream,而ServletInputStream是一个抽象类,因此从字符串获得字节流的方法中,使用了扩展至ServletInputStream的子类,MyServletInputStream类如下:
public class MyServletInputStream extends ServletInputStream {private ByteArrayInputStream byteArrIn = null;
public MyServletInputStream(String data) {
byteArrIn = new ByteArrayInputStream(data.getBytes());
}
@Override
public int read() throws IOException {
return byteArrIn.read();
}
}
MyServletRequestWrapper包装类,需要通过过滤器来应用:
public class RequestFilter implements Filter {
private FilterConfig filterConfig;
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//用包装类来替换http请求对象
chain.doFilter(new MyServletRequestWrapper((HttpServletRequest) request), response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
}
在web.xml文件中配置过滤器
<filter>
<filter-name>myFilter</filter-name>
<filter-class>xmlrpc.filter.RequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/MyXmlRpcServer</url-pattern>
</filter-mapping>
至此,客户端请求的内容,已被替换掉了。