在J2ME平台上构建你的邮件程序
发表于:2007-07-01来源:作者:点击数:
标签:
在J2ME平台上构建你的邮件程序 Jacky Pan Table of Contents 1. 教程的介绍和程序的安装 2. 程序的结构 3. 界面的设计 4. 账户的管理 5. MIDlet和Servlet的 网络 连接 6. Servlet和JavaMail 7. 简单的XML 8. 小结 1.教程的介绍和程序的安装 本教程讲述了如何
在J2ME平台上构建你的邮件程序
Jacky Pan
Table of Contents
1. 教程的介绍和程序的安装
2. 程序的结构
3. 界面的设计
4. 账户的管理
5. MIDlet和Servlet的
网络连接
6. Servlet和JavaMail
7. 简单的XML
8. 小结
1.教程的介绍和程序的安装
本教程讲述了如何在J2ME平台上编写一个简单的邮件应用程序,包括界面的设计,邮件的发送/接受,邮件账户的创建/修改/删除,后台Servlet的编写。
为了运行本教程所带的演示程序,您需要安装下列软件:
1. WTK2.0 (java.sun.com)
2. Apache Tomcat (www.apache.org)
安装和运行示例程序的步骤:
1. 从http://groups.yahoo.com/group/OriTec/files/下载MicroMail Beta.zip(包括了源代码和二进制文件)
2. 解压MicroMail Beta.zip至$TMP
3. 在$WTK/apps下建一新目录MicroMail
4. 拷贝$TMP/src/client/* 至 $WTK/apps/MicroMail/src/
5. 拷贝$/TMP/bin/server/mail.war至$TOMCAT/webapps/
6. 运行Tomcat
7. 运行WTK2.0, “Open Project” 并选中MicroMail
8. 设置URL为MailAgent的地址http://server/mail/MailAgent
2. 程序的结构
采用Client-Web Server-Mail Server三层架构,如图1。
MIDlet
(Cell Phone)
Servlet
(Web Server)
Mail Server
My Application
Figure 1
Cell Phone将请求(接受/发送邮件)传给Web Server,Web Server将这些http请求转换成对POP3或 SMTP Server的请求。POP3/SMTP Server执行相应的请求,并将相应通过Web Server返回给Cell Phone.
客户端(PDA/手机)为J2ME平台上的程序。MIDP2提供了一些基本网络连接的API。利用这些API可以使得J2ME程序可以向远端发出Http请求,并接受响应,传递数据流。
MailAgent 为Servlet,用来接收来自客户端的请求,并调用Java Mail API,将这些请求转变成对远端Mail Server 的请求,同时将Mail Server的响应传给客户端。
那么为什么要采用这样的架构了?这是因为MIDP2.0只支持HTTP协议, 而不支持POP3 和 SMTP等其它应用层协议,而J2EE提供了完整的Java Mail的API,所以考虑通过一个Servlet将Http请求转换成POP3或SMTP请求。另一个原因是,很多运营商可能只提供有限的网络访问的能力,而通过一个agent则提供了程序部署的灵活性。
下面简单介绍一下源码的结构,在客户端,ui包中的类定义程序的用户界面,utility包中的类定义了
数据库的操作,网络的连接,XML的解析等,mail包中的类定义了邮件账户,邮件的头。
ui package:
AccountForm.java
AccountsList.java
Confirm.java
ConfirmListener.java
MailMIDlet.java
MessageList.java
ProgressForm.java
SendMessage.java
WriteContent.java
utility package:
DBOperator.java
HeadParser.java
NetAdapter.java
Networker.java
ParserListener.java
mail package:
MailAccount.java
MessageHead.java
服务器端只有一个文件MailAgent.java,包含了一个servlet,用来做midlet和邮件服务器的桥梁。
3. 界面的设计
MIDP2.0提供了大量的API供
开发者创建和控制用户界面。在包javax.microedition.lcdui中提供了List, Form, TextBox, Alert等Screen组件,在Form中可以包含一系列Item,如ChoiceGroup, TextField, StringItem等。包中还提供了Command组件,以及相应的Listener。
源文件ui/MailMIDlet.java定义了程序的入口以及MicroMail的主页面。如图2主页面是一个List,显示了几个功能模块,包括添加账户,修改/删除账户,接受邮件,发送邮件。
Figure 2
public MailMIDlet()
{
display = Display.getDisplay(this);
/* 创建List */
mainList = new List("MicroMail0.1", Choice.IMPLICIT);
/* 创建两个控制按钮OK和Exit */
cmOK = new Command("OK", Command.OK, 1);
cmExit = new Command("Exit", Command.EXIT, 1);
/* 向mainlist中添加内容 */
mainList.append("Add Account", null);
mainList.append("Edit Account", null);
mainList.append("Receive Message", null);
mainList.append("Send Message", null);
/* 为mainlist添加Command */
mainList.addCommand(cmOK);
mainList.addCommand(cmExit);
/* MailMIDlet实现了CommandListener接口,可以作为监听器 */
mainList.setCommandListener(this);
……
}
通过AccountForm(源文件ui/AccountForm.java),用户可以添加和修改邮件账户,如图3。
Figure 3a Figure 3b
AccountForm包含了6个TextField,分别显示邮件账户的6个属性。account为账户名,也是账户的唯一标识,不可重名。address为邮件的地址,如bill@ms.com。 user, password是在邮件服务商注册的用户名和密码。pop3, smtp是pop3服务器和smtp服务器的名字或地址。
private void setContent(MailAccount macc)
{
account = new TextField("Account: ", "", 20, TextField.ANY);
address = new TextField("Address; ", "", 40, TextField.EMAILADDR);
user = new TextField("User Name: ", "", 20, TextField.ANY);
password = new TextField("Password: ", "", 20, TextField.PASSWORD);
pop3 = new TextField("POP3 Server: ", "", 20, TextField.ANY);
smtp = new TextField("SMTP Server: ", "", 20, TextField.ANY);
if (macc != null)
{
account.setString(macc.accountName);
address.setString(macc.address);
user.setString(macc.username);
password.setString(macc.password);
pop3.setString(macc.POP3Server);
smtp.setString(macc.SMTPServer);
}
append(account);
append(address);
append(user);
append(password);
append(pop3);
append(smtp);
}
其它页面与AccountForm类似,在此不再赘述,请大家参照源代码和MIDP API的文档。
4. 账户的管理
邮件账户的创建,修改,删除都涉及到对数据记录的存取。MIDP提供了一种叫做记录管理系统(Record Management System)的机制来存储和访问数据。
javax.microedition.rms.RecordStore提供了一些API来操作这个系统。静态方法openRecordStore用来打开或创建一个RecordStore对象。方法addRecord, getRecord, deleteRecord, setRecord分别用来添加,访问,删除或修改RecordStore对象中的记录。
utility/DBOperator.java中封装了这些方法,从而实现添加,修改,删除邮件账户的操作。以添加帐户为例,下面的addRecord方法用来向RecordStore中添加一个记录,同时把这个记录所表示的邮件账户加到一个accounts中,accounts是一个Vecotr,用来存储系统中当前的账户。参数str包含了账户的所有信息,如账户名,地址,用户名,密码,pop3,smtp等,它们之间用空格隔开。
public void addRecord(String str)
{
int id;
byte[] rec = str.getBytes();
String record = str;
try
{
/* 向RecordStore中添加记录 */
id = rs.addRecord(rec, 0 , rec.length);
/* 同时把帐户添加到accounts向量中 */
MailAccount mailacc = MailAccount.createMailAccountFromString(id, str);
accounts.addElement(mailacc);
}
catch(RecordStoreException rse)
{
rse.printStackTrace();
}
}
5. MIDlet和Servlet的网络连接
MIDP的网络API在包javax.microedition.io中定义,其中HttpConnection提供了对HTTP协议的支持。
在文件utility/Networker.java中通过调用这些网络API实现了接收当前信箱中邮件列表setMessageList,接受某一邮件的内容receiveMessage,发送邮件sendMessage等功能。以sendMessage为例。
public void sendMessage(final String url, final String formData)//send a message
{
/* 创建新的进程 */
Thread t = new Thread()
{
public void run()
{
HttpConnection http = null;
byte[] data = formData.getBytes();
try
{
/* 打开并返回一个HTTP连接 */
http = (HttpConnection) Connector.open(url);
......
/* 设置HTTP请求头 */
http.setRequestMethod(HttpConnection.POST);
http.set
RequestProperty("Content-Type", "application/x-www-form-urlencoded");
http.setRequestProperty("User-Agent",
"Profile/MIDP-1.0 Configuration/CLDC-1.0");
http.setRequestProperty("Content-Language", "en-US");
http.setRequestProperty("Accept", "application/octet-stream");
http.setRequestProperty("Connection", "close"); // optional
http.setRequestProperty("Content-Length", Integer.toString(data.length));
......
/* 打开输出流 */
OutputStream os = http.openOutputStream();
/* 写邮件数据 */
os.write(data);
/* 关闭输出流 */
os.close();
}
catch (IOException ioe)
{
......
}
finally
{
try
{
/* 关闭连接 */
if (http != null) http.close();
}
catch (IOException ignored) {}
}
}
};
/* 启动进程 */
t.start();
}
MIDlet通过HTTP连接向Servelet发出接受或发送邮件的请求,Servlet根据不同的请求向邮件服务器发出相应的请求,并将返回结果传给MIDlet。
6. Servlet和JavaMail
J2EE中提供了对邮件相关协议的支持,包javax.mail和包javax.mail.internet 中定义了JavaMail API。下面是MailAgent.java中Servlet处理接受邮件列表请求的代码片断。
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter out = response.getWriter();
String typeStr = request.getParameter("type");
int type = Integer.parseInt(typeStr);
String pop3Server;
String username;
String password;
Properties props;
String provider;
switch (type)
{
case RECEIVE_LIST:
/* 提取参数pop3服务器,用户名,密码 */
pop3Server = request.getParameter(paramPOP3);
username = request.getParameter(paramName);
password = request.getParameter(paramPass);
if (pop3Server == null || username == null || password == null)
{
out.print(STATUS_BAD);//xml?
return;
}
props = new Properties();
provider = "pop3";
try
{
/* 以指定的用户名和密码连接pop3服务器 */
Session session = Session.getDefaultInstance(props, null);
Store store = session.getStore(provider);
store.connect(pop3Server, username, password);
/* 得到INBOX收件箱 */
Folder inbox = store.getFolder("INBOX");
if (inbox == null)
{
out.print(STATUS_BAD);//xml?
return;
}
/* 打开收件箱 */
inbox.open(Folder.READ_ONLY);
/* 得到收件箱中的邮件列表,并写到 XML中 */
writeXML(inbox.getMessages(), out);
inbox.close(false);
store.close();
}
catch(Exception e)
{
out.println(e.getMessage());
e.printStackTrace();
}
out.close();
break;
case RECEIVE_MESSAGE:
......
break;
case SEND_MESSAGE:
......
break;
}
}
J2EE程序的编译和部署请大家参照相关的书籍或文档。http://www.tusc.com.au/tutorial/html/ 的<<Tutorial for building J2EE Applications using JBOSS and ECLIPSE>>是一篇不错的文档。
7. 简单的XML
在MIDlet接受邮件时,首先向Servlet请求传送邮箱中当前邮件的列表。列表中包括了邮件的头部信息,包括发送者的地址,邮件的主题,发送的时间等,如图4。
Figure 4
在邮件主题中可能包括任何字符,所以没有办法用某一特殊字符分隔这些信息,而XML正好适合传输这种具有特定格式的信息。在Servlet端,把有用的邮件头部信息作为XML的元素写到输出流中。
private void writeXML(Message[] messages, PrintWriter out)
{
out.println("<?xml version=\"1.0\"?>");
out.println("<mail>");
if (messages == null)
{
out.println("<error>No Mail</error>");
}
try
{
int j = 0;
for (int i = messages.length -1; i >= 0; i--)
{
out.println("<message>");
/* 写邮件头 */
out.println("<from><![CDATA[" + InternetAddress.toString(messages[i].getFrom()) + "]]></from>");
out.println("<subject><![CDATA[" + messages[i].getSubject() + "]]></subject>");
out.println("<date>" + messages[i].getSentDate().toLocaleString() + "</date>");
out.println("<index>" + i + "</index>");
out.println("</message>");
j++;
if (j > 9)
{
/* 一次只看10个邮件 */
break;
}
}
}
catch(MessagingException me)
{
out.println("<error>" + me.toString() + "</error>");
}
out.println("</mail>");
}
在MIDlet端再解析这个XML,在J2ME平台上有许多免费的XML Parser,kxml就是其中的一个。可以从 http://kxml.enhydra.org/ 下载kXML 1.21的源代码,jar文件以及API文档。
下面是utility/HeadParser.java中处理XML的代码片断
public void parse(InputStream in) throws IOException
{
Reader reader = new InputStreamReader(in);
XmlParser parser = new XmlParser(reader);
ParseEvent pe = null;
parser.skip();
/* 读一个名字为mail的event */
parser.read(Xml.START_TAG, null, "mail");
boolean trucking = true;
boolean first = true;
while (trucking)
{
/* 读取下一个event */
pe = parser.read();
if (pe.getType() == Xml.START_TAG)
{
/* 得到event的名字 */
String name = pe.getName();
if (name.equals("message"))
{
String from = null;
String subject = null;
String date = null;
int index = -1;
while ((pe.getType() != Xml.END_TAG) ||
(pe.getName().equals(name) == false))
{
pe = parser.read();
if (pe.getType() == Xml.START_TAG &&
pe.getName().equals("subject"))
{
pe = parser.read();
/* 得到event的内容 */
subject = pe.getText();
}
else if (pe.getType() == Xml.START_TAG &&
pe.getName().equals("from"))
{
pe = parser.read();
from = pe.getText();
}
else if (pe.getType() == Xml.START_TAG &&
pe.getName().equals("date"))
{
pe = parser.read();
date = pe.getText();
}
else if (pe.getType() == Xml.START_TAG &&
pe.getName().equals("index"))
{
pe = parser.read();
index = Integer.parseInt(pe.getText());
}
}
/* 把邮件头交给监听器处理 */
headListener.itemParsed(from, subject, date, index);
}
else //Non Message block
{
while ((pe.getType() != Xml.END_TAG) ||
(pe.getName().equals(name) == false))
pe = parser.read();
}
}
if (pe.getType() == Xml.END_TAG &&
pe.getName().equals("mail"))
{
trucking = false;
}
}
具体的API的用法请大家参照kxml的文档。JSR172 (J2ME Web Services Specification)提出了在J2ME平台上处理XML的规范,有兴趣的朋友可以到jcp的网站 (http://www.jcp.org) 上看看,但目前可能还没有厂商或组织的实现。
8. 小结
本文介绍了J2ME平台上邮件程序的编写,涉及的
知识点有:
1. J2ME的UI
2. Record Store
3. J2ME的网络连接 / J2ME 和J2EE之间数据的传递
4. Parsing XML in J2ME
5. 简单的Servlet
6. Java Mail APIs
参考资料有:
1. MIDP2.0 Spec, http://www.jcp.org
2. <<Core J2ME>>, http://www.corej2me.com/
3. <<J2ME in Nutshell>>, http://www.oreilly.com
4. Tutorial for building J2EE Applications using JBOSS and ECLIPSE, http://www.tusc.com.au/tutorial/html/
原文转自:http://www.ltesting.net