Linux平臺,使用JavaComm3 API及SMSLib項目實現在Web Application中發送手機短信的功能

一、需求:

     在一個Flash程序中通過點擊程序中的數字鍵盤,輸入手機號碼,提交後通過http請求

例如:http://localhost:8080/golf/stterm/im.jsp?email=yl%2Ewang%40elitechmedia%2Ecom&mobileNumber=_phoneNumber,將信息提交到一個Web服務,完成發送短信到指定手機號碼和發送Email到指定的Address的功能。

二、平臺:Redhat5企業版

      服務器:apache-tomcat-6.0.26

      JDK:1.6

      Modem:華爲E176g3G無線網卡

三、調查

   1)通過調查得知java類庫中已實現了相關的串口通信API,通過JNI接口訪問本地代碼來實現串口通信。可以在以下網址獲得詳細信息

http://java.sun.com/products/javacomm/,串口通信問題得以解決。

   2)通過SMSLib庫實現Java程序發送短信

    SMS(Short Messaging Service)即短消息業務,是由Etsi所制定的一個規範(GSM 03.40 和 GSM03.38)。當使用其7-bits編碼時,可以發送最多160個字符;使用8-bit編碼,最多可以發送140個字符,通常無法直接通過手機顯 示;還有用16-bit編碼時,最多70個字符,被用來顯示Unicode(UCS2)文本信息,可以被大多數的手機所顯示。

目前程序中發送短信大致有三種途徑:
1、 向當地的電信部門申請網關,不需要額外的設備,利用對方提供的API調用程序發送短信,適用於大型的通信公司。
2、 藉助像GSM MODEM之類的設置(支持AT指令的手機也行),通過數據線連接電腦來發送短信,這種方法比較適用於小公司及個人。要實現這種方式必須理解串口通信、AT指令、短信編碼、解碼。
3、 利用網站實現,由網站代發短信數據,對網站依賴性太高,對網絡的要求也比較高,不適於進行項目開發。
 

    所謂AT,即Attention。AT命令集是從Terminal Equipment或Data Terminal Equipment向Terminal Adapter或Data Circuit Terminating Equipment發送的,通過TA、TE發送AT命令來控制Mobile Station的功能與GSM網絡業務進行交互。我們可以通過AT命令進行呼叫短信、電話本、數據業務、補充業務、傳真等方面的控制。 由於AT指令操作是非常之簡單的,我們完全可以自己寫組件完成相關操作,而且針對聯通、移動、小靈通等不同的服務需求,自制組件反而更容易控制及擴充。

在Java編程中可以通過Java Comm進行手機與電腦的串口通訊,並通過AT指令控制手機操作。在Google code上有個SMSLib項目,是一個以AT指令實現手機操作的組件(http://code.google.com/p/smslib/downloads/list)。

至此,有了SMSLib項目,解決了許多問題,不用自己去寫底層的 AT指令,方便了很多,感謝SMSLib項目作出的貢獻。

 

四、開始

    將以上兩個項目相關的類庫下載後,按照Doc中的安裝指導完成安裝。只要按照文檔正確的安裝,即可實現發送短信的功能。在這裏我想寫的重點是在開發中遇到的一些問題。至於類庫的使用請參照文檔,這裏就不再詳細說明了

    需要注意的是,Java Comm2只支持Windows平臺串口通信,如果目標平臺爲Linux,需要下載Java Comm3類庫,或者使用開源類庫RXTX替代Java Comm3。RXTX的安裝可參考如下資源:

http://www.agaveblue.org/howtos/Comm_How-To.shtml

    以下是程序運行中可能出現的一些異常,以及我總結的異常處理方法

 (1)Windows平臺,在java工程中運行正常,移植到web application,連接串口就會報告javax.comm.NoSuchPortException

     折騰了很久,收到一個帖子的啓發,提到了內置和外置的tomcat,其實問題還是出在jdk版本的問題,smslib必須用jdk1.6的版本才能正常運行,在myeclipse內置的tomcate中無法啓動,那是因爲內置tomcat採用了jdk1.5.

    config內置的tomcat,可以看到jdk採用的是myclipse內置的jdk,把它修改爲1.6的jdk,或者配置外置的tomcat,都可以解決問題。

(2)報告org.smslib.GatewayException: Comm library exception: java.lang.RuntimeException: java.io.IOException: Error setting serial port parameters

   在Linux平臺運行程序,拋出以上異常,是因爲此3G網卡已經撥上了號,在使用中,使用ifdown ppp0命令,停掉3G後,再運行即可正常使用。

   同樣的原因在Windows平臺將拋出

org.smslib.GatewayException: Comm library exception: java.lang.RuntimeException: javax.comm.PortInUseException: Port currently owned by Unknown Windows Application

異常,解決方法同樣是停掉3G網卡

  可見串口被佔用後其他程序就不能使用了

  ps:奇怪的是,同樣的原因在Linux和Windows平臺拋出不同的異常。

(3)解決了(1)中的問題後,在Windows平臺的Web application中可正常運行,於是決定將此Web Application移植到Linux平臺,這也是我的終極目標。本來想問題不大,事實是,在移植過程中遇到了很大的困難。

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

按照文檔中的要求使用Java Comm3替換了Java Comm2,然後將此Web Application發佈到tomcat下運行,出現如下錯誤:

javax.comm:  Error loading javax.comm.properties!

null

javax.comm: platform driver class name = null

(Check 'driver' property in javax.comm.properties)

Googling了2天,居然沒找到一個解決方案,鬱悶中。。。

 

沒辦法了,先試試Linux平臺下Java application能不能正常運行。於是下載了myeclipse的linux版本,安裝,寫程序,運行,No Problem。由此分析,應該是Java項目和Java Web Application在運行上的某種機制不同。有人提到Tomcat的沙箱安全機制導致了從IE中運行程序不能訪問javax.comm.properties文件導致了此問題。

參考:http://www.sftw.umac.mo/~utako/research/SmartTicket/collect/applets-faq-qna.html

 

雖然有這個可能,但是,沒有人提出一個解決方案。最後想到可從Java Comm3的源代碼中找問題。

使用java Decompiler反編譯了拋出異常的類CommPortIdentifier,CommProperties

終於弄清楚了,原來由CommProperties類執行加載javax.comm.properties文件,讀取配置文件中設置的屬性。之後CommPortIdentifier類通過讀取的driver加載相應的驅動類。

再查看Linux平臺javax.comm.properties文件中的內容,其中設置了以下屬性:

××××××××××××××××××××××××××××××××××××××××××××××

# Implementation specific driver
driver=com.sun.comm.LinuxDriver                    (1)

# Paths to server-side serial port devices
serpath0 = /dev/ttyUSB0                                  (2)我修改過
serpath1 = /dev/ttyUSB1

# Paths to server-side parallel port devices
parpath0 = /dev/parport0                                 (3)
parpath1 = /dev/parport1

××××××××××××××××××××××××××××××××××××××××××××××

(1)處爲需要加載的串口驅動的類名,此類在Java Comm3中,如果是使用RXTX的話,此處的值需要改爲

   gnu.io.RXTXCommDriver

(2)處屬性設置了Modem在linux平臺的串口設備符,原版的javax.comm.properties文件中此處的值爲:

××××××××××××××××××××××

serpath0 = /dev/ttyS0
serpath1 = /dev/ttyS1

××××××××××××××××××××××

/dev/ttyS0,ttyS1是Modem設備在Linux平臺的標準設備符。由於我使用的華爲E176G映射到Linux平臺的設備符爲

/dev/ttyUSB_utps_modem,

所以我創建了一個link,將

/dev/ttyUSB0

映射爲

dev/ttyUSB_utps_modem

再將javax.comm.properties文件中的serpath0的值修改爲

/dev/ttyUSB0

(3)處的屬性值設置了Linux平臺的並口設備符,由於沒有使用,未作修改。

基於以上知識。考慮安全機制導致不能正確加載javax.comm.properties文件,所以決定修改源代碼,直接加載驅動類com.sun.comm.LinxuDriver

行動,將CommProperties類中加載javax.comm.properties文件的語句註釋掉,然後在CommPortIdentifier類中

註釋掉從配置文件中讀取驅動的代碼,將需要加載的驅動名賦值爲:com.sun.comm.LinxuDriver。

××××××××××××××××××××××××××××××××××××××××××××××××××××××

反編譯處的代碼片段如下,只貼出了修改過的代碼,可自行反編譯查看完整代碼

CommProperties{

...

 

static
  {
    try
    {
      if ((CommProperties.propFilename = findPropFile("javax.comm.properties")) != null) {

        //註釋掉加載javax.comm.properties文件的代碼
        //commProps.load(new BufferedInputStream(new FileInputStream(new File(propFilename))));
      }
      else
        System.err.println("javax.comm:  Can't find javax.comm.properties!");
    }
    catch (Exception e) {
      System.err.println("javax.comm:  Error loading javax.comm.properties!");

      System.err.println(e.getMessage());
    }
  }

...}

CommPortIdentifier {

...

 static
  {

    //String driverName = null;

//修改爲直接賦值com.sun.comm.LinuxDriver
    String driverName = "com.sun.comm.LinuxDriver";
    try {

//以下三行註釋,不同屬性文件中讀取驅動類
      //if (((driverName = CommProperties.getProperty("driver")) == null) &&
      //  ((driverName = CommProperties.getProperty("DRIVER")) == null))
      //  driverName = CommProperties.getProperty("Driver");
      commDriver = loadDriver(driverName);
    } catch (Throwable t) {
      System.err.println("");
      t.printStackTrace();
    }

××××××××××××××××××××××××××××××××××××××××××××××××××××××

大功告成,使用編譯後的這兩個類替換掉comm.jar包中的同名類,然後使用jar命令重新打包爲comm.jar,使用這個修改版的comm.jar文件運行web application應用,不再出現

javax.comm:  Error loading javax.comm.properties!

null

javax.comm: platform driver class name = null

(Check 'driver' property in javax.comm.properties)

異常,可是新的異常由出現了,異常描述找不到端口,同異常類型(1)

 

 

由此想到,忽略了javax.comm.properties中關於serpath0,serpath1的屬性值,此爲註冊串口設備符需要用到的值。

再查看反編譯的類,發現在類PlatformPortBundle中通過讀取javax.comm.properties文件中的屬性serpath0,serpath1將相應的設備符註冊給程序,完整代碼如下:

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

原版:

package com.sun.comm;

public class PlatformPortBundle extends PathBundle
{
  public PlatformPortBundle()
  {
    String portName = null;
    String portPath = null;

    for (int i = 0; i < 1024; ++i) {
      portName = "serpath" + i;
      if ((portPath = CommProperties.getProperty(portName)) == null) break;
      super.add(portPath, 1);
    }

    for (int i = 0; i < 1024; ++i) {
      portName = "parpath" + i;
      if ((portPath = CommProperties.getProperty(portName)) == null) return;
      super.add(portPath, 2);
    }
  }
}

 

修改版:直接註冊爲/dev/ttyUSBi(i=0,1)

package com.sun.comm;

public class PlatformPortBundle extends PathBundle
{
  public PlatformPortBundle()
  {
    //String portName = null;
    String portPath = null;

    for (int i = 0; i < 2; ++i) {
      //portName = "serpath" + i;
      portPath = "/dev/ttyUSB" + i;
      //if ((portPath = CommProperties.getProperty(portName)) == null) break;
      super.add(portPath, 1);
    }

    for (int j = 0; j < 2; ++j) {
      //portName = "parpath" + j;
      portPath = "/dev/parport" + j;
      //if ((portPath = CommProperties.getProperty(portName)) == null) return;
      super.add(portPath, 2);
    }
  }
}

替換PlatformPortBundle.class文件後重新打包comm.jar,運行,一切正常,討厭的Exception再也不出現了。

至此,Linux平臺使用SMSLib和JavaComm3API,實現在Java Web Application應用中發送手機短信的項目完成,希望本人在項目中遇到的問題及相關的解決方案對大家有所幫助。

PS:修改版的comm.jar已上傳到csdn中,需要的朋友請從以下鏈接下載

 http://dldx.csdn.net/fd.php?i=990623329111710&s=51294bcdf7487d8ce54d50b300edccc5

 由於我在修改後的源文件中直接將串口設備符註冊爲/dev/ttyUSB0,/dev/ttUSB1,所以如果你的Modem的設備符不是/dev/ttyUSB0請自行創建一個link,命名爲/dev/ttyUSB0

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