在J2ME平臺上構建你的郵件程序

 

J2ME平臺上構建你的郵件程序

Jacky Pan

 

Table of Contents

1.       教程的介紹和程序的安裝

2.       程序的結構

3.       界面的設計

4.       賬戶的管理

5.       MIDletServlet的網絡連接

6.       ServletJavaMail

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.       設置URLMailAgent的地址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 ServerWeb 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 MailAPI,所以考慮通過一個ServletHttp請求轉換成POP3SMTP請求。另一個原因是,很多運營商可能只提供有限的網絡訪問的能力,而通過一個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, AlertScreen組件,在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);

       /* 創建兩個控制按鈕OKExit */

    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包含了6TextField,分別顯示郵件賬戶的6個屬性。account爲賬戶名,也是賬戶的唯一標識,不可重名。address爲郵件的地址,[email protected] user, password是在郵件服務商註冊的用戶名和密碼。pop3, smtppop3服務器和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包含了賬戶的所有信息,如賬戶名,地址,用戶名,密碼,pop3smtp等,它們之間用空格隔開。

 

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. MIDletServlet的網絡連接

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.setRequestProperty("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. ServletJavaMail

J2EE中提供了對郵件相關協議的支持,包javax.mail和包javax.mail.internet 中定義了JavaMail API。下面是MailAgent.javaServlet處理接受郵件列表請求的代碼片斷。

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 Parserkxml就是其中的一個。可以從 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();

  /* 讀一個名字爲mailevent */

  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.  J2MEUI

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/

 

 


發佈了53 篇原創文章 · 獲贊 1 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章