从Java EE到Java ME的通讯(3)

2007-10-09     浏览:1361     来源:SOFT6
关键词:  Java      Jav  

  服务器端实现

  相比手机端,服务器端实现起来容易多了。采用的数据库是MySQL,并使用Proxool连接池,去年在我的blog上也做了引见,可参考《Proxool 0.9.0RC1 发布》一文。目前最新版本是0.9.0RC3,Proxool的作者基本上一年才仅仅更新一次RC后面的版本,可能明年才更新到0.9.0RC4,可谓慢工出细活。下面是服务器端的GoodsFindAuto Servlet类,由于是2年前的代码,那时为了简便起见,我把需要在DAO里做的事情都搬到这个Servlet了,另外代码里面应该用事务保证,我也偷懒了。

  package com.forbidden.airtransport.servlet;

  import java.io.DataOutputStream;

  import java.io.IOException;

  import java.io.OutputStream;

  import java.sql.Connection;

  import java.sql.DriverManager;

  import java.sql.PreparedStatement;

  import java.sql.ResultSet;

  import java.sql.SQLException;

  import java.sql.Timestamp;

  import java.text.SimpleDateFormat;

  import java.util.Date;

  import javax.servlet.ServletException;

  import javax.servlet.http.HttpServlet;

  import javax.servlet.http.HttpServletRequest;

  import javax.servlet.http.HttpServletResponse;

  import com.forbidden.airtransport.util.RegCode;

  /* 货主找车Servlet

  * @author rosen jiang

  * @since 2005-12

  */

  public class GoodsFindAuto extends HttpServlet {

  /**

  * 构造函数

  */

  public GoodsFindAuto() {

  super();

  }

  /**

  * 销毁方法

  */

  public void destroy() {

  super.destroy();

  }

  /**

  * 货主找车功能

  *

  * @param request HttpServletRequest

  * @param response HttpServletResponse

  * @throws ServletException

  * @throws IOException

  */

  public void doGet(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException {

  //电话区号实际城市名互转类

  RegCode rc = new RegCode();

  //用户名,用于记载查询日志。

  String userName = request.getParameter("userName");

  //页数

  int page = 0;

  String strPage = request.getParameter("page");

  //每页条数

  int perPage = 0;

  String strPerPage = request.getParameter("perPage");

  //车辆出发地

  String autoFrom = request.getParameter("autoFrom");

  //车辆目的地

  String autoTarget = request.getParameter("autoTarget");

  //发布日期

  String pubDate = request.getParameter("pubDate");

  //查询日志

  String writeLog = "insert into search_log (userName,flag,f_rom,target,serDate)" +

  " values (?,?,?,?,?)";

  //计算总记录数

  String countRow = "select count(*) from auto_back where autoFrom=? and autoTarget=? " +

  "and TO_DAYS(regDate)=TO_DAYS(?)";

  //查询结果

  String finSql = "select * from auto_back where autoFrom=? and autoTarget=? " +

  "and TO_DAYS(regDate)=TO_DAYS(?) limit ?,?";

  //连接信息

  Connection conn = null;

  PreparedStatement stm = null;

  ResultSet rs = null;

  //构建输出流

  response.setContentType("application/octet-stream");

  OutputStream ops = response.getOutputStream();

  DataOutputStream dos = new DataOutputStream(ops);

  try {

  if(userName==null || strPage==null || strPerPage==null || autoFrom==null ||

  autoTarget==null || pubDate==null){

  dos.writeUTF("非法请求!");

  }else{

  page = Integer.parseInt(strPage);

  perPage = Integer.parseInt(strPerPage);

  //电话区号转换到实际地址

  autoFrom = rc.convent(autoFrom);

  autoTarget = rc.convent(autoTarget);

  //获取连接

  conn = DriverManager.getConnection("proxool.automy");

  //记录请求信息

  stm = conn.prepareStatement(writeLog);

  stm.setString(1,userName);

  stm.setString(2,"2");

  stm.setString(3,autoFrom);

  stm.setString(4,autoTarget);

  Date sDate = new Date();

  stm.setTimestamp(5,new Timestamp(sDate.getTime()));

  stm.executeUpdate();

  //计算结果集总数

  stm = conn.prepareStatement(countRow);

  Date date = new Date(Long.parseLong(pubDate));

  SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

  String Fdate = formatter.format(date);

  stm.setString(1,autoFrom);

  stm.setString(2,autoTarget);

  stm.setString(3,Fdate);

  rs = stm.executeQuery();

  rs.next();

  int row = rs.getInt(1);

  if(row==0){

  dos.writeUTF("未找到你要匹配的数据。");

  }else{

  //进行查询

  stm = conn.prepareStatement(finSql);

  int rows = 0;

  if(page==1){

  rows = 0;

  }else{

  rows = perPage*((page-1)/4);

  }

  stm.setString(1,autoFrom);

  stm.setString(2,autoTarget);

  stm.setString(3,Fdate);

  stm.setInt(4,rows);

  stm.setInt(5,perPage);

  rs = stm.executeQuery();

  dos.writeUTF(""); //设置前两个字节为空

  dos.writeUTF(row+""); //记录总数

  if(row%perPage!=0){

  dos.writeUTF(Integer.toString(1+(row/5))); //总页数

  }else{

  dos.writeUTF(Integer.toString((row/5))); //总页数

  }

  dos.writeUTF(Integer.toString(page)); //当前页

  while(rs.next()){

  //创建业务数据输出流

  String resString = rs.getString(3)+"&"+rs.getString(4)

  +"&"+rs.getString(6)+"&"+rs.getString(5)

  +"&"+rs.getString(9) +"&"

  +rs.getString(8)+"&"+rs.getString(10);

  dos.writeUTF(resString);

  }

  }

  }

  //数据长度

  response.setContentLength(dos.size());

  } catch (Exception e) {

  e.printStackTrace();

  dos.writeUTF("服务器异常!"+e.toString());

  }finally{

  try {

  rs.close();

  stm.close();

  conn.close();

  } catch (SQLException e) {

  e.printStackTrace();

  }

  dos.flush();

  dos.close();

  ops.close();

  }

  }

  /**

  * post方法

  *

  * @param request HttpServletRequest

  * @param response HttpServletResponse

  * @throws ServletException

  * @throws IOException

  */

  public void doPost(HttpServletRequest request, HttpServletResponse response)

  throws ServletException, IOException {

  }

  /**

  * 初始化方法

  *

  * @throws ServletException

  */

  public void init() throws ServletException {

  }

  }

  Servlet 应该都很熟悉了,那就直奔”doGet()”方法。”RegCode rc = new RegCode()”构造一个电话区号到实际城市名互转的实现,实现方式也就是在数据库建立一个“电话区号—城市名”的一一对应关系。接着通过”response.setContentType("application/octet-stream")”代码声明输出类型。可以搞破坏了,我在浏览器中写上” http://127.0.0.1:8080/AirTransport/servlet/GoodsFindAuto”,GoodsFindAuto Servlet发现没有收到正确的查询条件,马上通过”dos.writeUTF("非法请求!")”代码抛出消息。

  业务上要求无论有没有符合条件的结果,都要记录用户查询信息;在记录完查询信息后开始根据条件去数据库匹配,如果匹配结果为0就通过”dos.writeUTF("未找到你要匹配的数据。")”代码抛出消息;如果>0就取出详细数据,然后通过”dos.writeUTF(resString)”代码循环输出数据流,这和之前在手机端实现中说到的“根据表中字段的顺序再在中间加”&”进行分割的字符串”完全匹配,另外还不能忘了通过”dos.writeUTF("")”设置前两个字节为空,让手机端明白有业务数据返回。最后,无论服务器端发生任何异常,都可以通过”dos.writeUTF("服务器异常!"+e.toString())”代码告诉手机端。

  如何做得更好

  开发Java ME应用给我最大的感受是“模拟器是不可靠的(只能相信它70%)”,模拟器运行成功,但是手机上却出现千奇百怪的问题,遇到这样的困惑只有自己多花时间、多请教他人才能解决了。关于Java ME程序的调试,网上有一大把说明,我就不拿来引用了,不过我照着说明修改了Eclipse,始终有点问题。另外,索爱和诺基亚都能正常运行,但摩托罗拉手机始终有中文乱码,这个问题我还没解决。如果你很在意劳动成果被他人窃取,那么推荐使用ProGuard一个很好用的压缩、优化、混肴器,在EclipseME插件里有相应的配置,在这里引用一句话“混淆只是延缓了反编译的时间”,自己想想也行,至少有那么一点安全感。另外还有安全性的问题,既然以HTTP方式提供服务,那么就应该实现一种安全策略,我考虑的是类似HTTP Session的方式,在登录成功后服务器端生成以时间戳为参考的随机数返回给手机端,然后接下来的交互手机端必须提供这个随机数进行服务器端验证,成功后才能获取业务数据,另外还要提供超时机制。

  我们知道连接GPRS很慢,但也要关注这样的用户提问:“为什么不能订阅本月内成都到上海的所有车辆呢?每天没事的时候打开程序让它在后台运行并自动同步数据保存在手机上,当符合条件的车辆出现时,能自动提醒我;不光如此,我还要在月底翻阅这些所有符合条件的车辆。”

  依照现有方式,短信推送可实现自动提醒功能,但是无法实现数据浏览(短信字数有限);WAP方式可以实现数据浏览,但是无法实现提醒功能,除非WAP浏览器定时刷新页面或支持Ajax,就算支持也不能实现离线数据浏览!

  问题的解决办法——“在手机上建立分布式数据库。”目前db4o西班牙团队领导的db4oME项目正在开发中,该项目能帮助实现手机分布式数据库的遐想。我们可以再想得更远点,是不是有了db4oME就能解决上面的问题?我认为可以解决,不过又会产生更多的问题,比如Java能管理手机上多大的存储空间,是否可以把数据存放在例如TF卡上,如果可以放在TF卡上那么检索效率如何,数据文件是否安全……

  要实现遐想并解释上面的问题有待于广大开发者的共同努力,把自己的业余时间都投入在类似db4oME这样的技术上,如果这样的分布式数据库能成功实现,我想这会给移动计算带来一场革命,更多依托网络、数据库的手机应用程序将会诞生,发挥Java强大的优势!