<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 1,  comments - 25,  trackbacks - 0
      2013年4月5日
         摘要: 這兩天又做了個Execute Jar Project.覺得以前的Path的知識都忘的差不多了,決定自己總結一下。1. 獲取類路徑    返回類路徑內容,一般為:lib1.jar;lib2.jar。1System.getProperty("java.class.path");     在命令行運行Java程序時...  閱讀全文
    posted @ 2013-04-05 13:38 Daniel 閱讀(401) | 評論 (1)編輯 收藏
      2012年7月23日

    2.1-1這題可以參照書上17自己給出過程,這里就略去了。

    2.1-2 先給出書上insertion-sort的C源代碼吧,然后再給出按照非升序的代碼:

    課本中(非降序的)insertion-sort代碼:

     

    1. void insertion_sort(int *A, int n)  
    2. {  
    3.     int i,j;  
    4.     int key; 
    5.  
    6.     for(i = 1; i < n; i++)  
    7.     {  
    8.         j = i - 1;  
    9.         key = A[i]; 
    10.  
    11.         while(j >= 0 && A[j] > key)  
    12.         {  
    13.             A[j+1] = A[j];  
    14.             j = j - 1;  
    15.         } 
    16.  
    17.         A[j+1] = key;  
    18.     }  

     

    在這題中,只要講非降序改成非升序排序,所以改后代碼如下:

     

    1. void insertion_sort(int *A, int n)  
    2. {  
    3.     int i,j;  
    4.     int key; 
    5.  
    6.     for(i = 1; i < n; i++)  
    7.     {  
    8.         j = i - 1;  
    9.         key = A[i]; 
    10.  
    11.         while(j >= 0 && A[j] < key)  
    12.         {  
    13.             A[j+1] = A[j];  
    14.             j = j - 1;  
    15.         } 
    16.  
    17.         A[j+1] = key;  
    18.     }  

     

    2.1-3這題給出偽代碼:

     

    1. int find(int *A , int n, int v) 
    2.  
    3.  
    4.     int  i = 0; 
    5.  
    6.     for( ; i < n; i++) 
    7.  
    8.     { 
    9.  
    10.         if(v == A[i]) 
    11.  
    12.             return i; 
    13.  
    14.     } 
    15.  
    16.   
    17.  
    18.     return NIL; 
    19.  

     

    2.1-4直接給出代碼:

     

    1. /*在A[]和B[]中,數組的最低位對應與二進制的高位,即如果一個二進制數是011100,用數組表示就是A[] = {0,1,1,1,0,0}*/ 
    2.  
    3. void add(int *A ,int *B, int *C, int n)  
    4. {  
    5.     int i, a, c = 0;  
    6.     int s;  
    7.     for(i = n - 1; i >= 0 ; i--)  
    8.     {  
    9.         s = A[i] + B[i];  
    10.          
    11.         C[i+1] = (s + c) % 2;  
    12.         c = (s + c) / 2;  
    13.     } 
    14.  
    15.     C[0] = c;  

     

    posted @ 2012-07-23 22:12 Daniel 閱讀(228) | 評論 (0)編輯 收藏
      2012年6月25日

    優化屏障和內存屏障

    優化屏障 (Optimization Barrier)

    編譯器編譯源代碼時,會將源代碼進行優化,將源代碼的指令進行重排序,以適合于CPU的并行執行。然而,內核同步必須避免指令重新排序,優化屏障(Optimization barrier)避免編譯器的重排序優化操作,保證編譯程序時在優化屏障之前的指令不會在優化屏障之后執行。

    Linux用宏barrier實現優化屏障,gcc編譯器的優化屏障宏定義列出如下(在include/linux/compiler-gcc.h中): 

    #define barrier() __asm__ __volatile__("": : :"memory")

    上述定義中,“__asm__”表示插入了匯編語言程序,“__volatile__”表示阻止編譯器對該值進行優化,確保變量使用了用戶定義的精確地址,而不是裝有同一信息的一些別名。“memory”表示指令修改了內存單元。

    內存屏障 (Memory Barrier)

    軟件可通過讀寫屏障強制內存訪問次序。讀寫屏障像一堵墻,所有在設置讀寫屏障之前發起的內存訪問,必須先于在設置屏障之后發起的內存訪問之前完成,確保內存訪問按程序的順序完成。

    讀寫屏障通過處理器構架的特殊指令mfence(內存屏障)、lfence(讀屏障)和sfence(寫屏障)完成,見《x86-64構架規范》一章。另外,在x86-64處理器中,對硬件進行操作的匯編語言指令是“串行的”,也具有內存屏障的作用,如:對I/O端口進行操作的所有指令、帶lock前綴的指令以及寫控制寄存器、系統寄存器或調試寄存器的所有指令(如:cli和sti)。

    Linux內核提供的內存屏障API函數說明如表2。內存屏障可用于多處理器和單處理器系統,如果僅用于多處理器系統,就使用smp_xxx函數,在單處理器系統上,它們什么都不要。

    posted @ 2012-06-25 12:05 Daniel 閱讀(604) | 評論 (0)編輯 收藏
         摘要: Preface最近看了一下<Java Concurrency In Practice> 這本書, 總體來說還是一本不錯的書, 不過粒度不夠細, 是從大的角度, 例如: 設計整體項目上如何考慮并發的多方面因素,不過總體上來說還是一本不錯的書,結合部分網絡上的資料,總結一下自己的知識,免的忘了。下面是一些最基本的知識,不想再寫了,反正網上多的是,挑了一篇還不錯的轉過來,大家要支持別人的成果...  閱讀全文
    posted @ 2012-06-25 09:37 Daniel 閱讀(1405) | 評論 (0)編輯 收藏
      2012年4月25日

    java nio小結

    根據網上的資料做些整理

    Java NIO API詳解

    http://www.tkk7.com/19851985lili/articles/93524.html

    這篇文章對nio的api講解比較全,可以幫助在宏觀上把握nio。

    BIO 方式使得整個處理過程和連接是綁定的,只要連接建立,無論客戶端是否有消息發送,都要進行等待處理,一定程度上浪費了服務器端的硬件資源,因此就有了NIO 方式。Java 對于 NIO 方式的支持是通過 Channel和 Selector 方式來實現,采用的方法為向 Channel注冊感興趣的事件,然后通過 Selector 來獲取到發生了事件的 key,如發生了相應的事件,則進行相應的處理,否則則不做任何處理,是典型的Reactor 模式,按照這樣的方式,就不用像 BIO 方式一樣,即使在沒有消息的情況下也需要占據一個線程來阻塞讀取消息,從而提升服務器的使用效率, 為實現 TCP/IP+NIO 方式的系統間通訊, Java 提供了 SocketChannel和 ServerSocketChannel兩個關鍵的類,網絡 IO 的操作則改為通過ByteBuffer 來實現,具體的基于 java實現TCP/IP+NIO 方式的通訊的方法如下所示。

    服務器端:

    復制代碼
    package com.eric.test.nio;

    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    import java.nio.channels.SocketChannel;

    public class NIOServer {
    /*標志數字*/
    private static int flag = 0;
    /*定義緩沖區大小*/
    private static int block = 4096;
    /*接收緩沖區*/
    private static ByteBuffer receiveBuffer = ByteBuffer.allocate(block);
    /*發送緩沖區*/
    private static ByteBuffer sendBuffer = ByteBuffer.allocate(block);
    /*定義Selector*/
    private Selector selector;

    public NIOServer(int port) throws IOException{
    //打開服務器套接字通道
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

    //服務器配置為非阻塞
    serverSocketChannel.configureBlocking(false);

    //檢索與此服務器套接字通道關聯的套接字
    ServerSocket serverSocket = serverSocketChannel.socket();

    //進行服務的綁定
    serverSocket.bind(new InetSocketAddress(port));

    //通過open()方法找到Selector
    selector = Selector.open();

    //注冊到selector
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    System.out.println("Server Start -----8888:");
    }
    //監聽
    public void listen() throws IOException{

    while(true){
    //監控所有注冊的 channel ,當其中有注冊的 IO 操作可以進行時,該函數返回,并將對應的 SelectionKey 加入 selected-key set
    selector.select();

    //Selected-key set 代表了所有通過 select() 方法監測到可以進行 IO 操作的 channel ,這個集合可以通過 selectedKeys() 拿到
    Set<SelectionKey> selectionKeys = selector.selectedKeys();

    Iterator<SelectionKey> iterator = selectionKeys.iterator();
    while(iterator.hasNext()){
    SelectionKey selectionKey = iterator.next();
    handleKey(selectionKey);
    iterator.remove();
    }
    }

    }
    //處理請求
    public void handleKey(SelectionKey selectionKey) throws IOException{

    //接受請求
    ServerSocketChannel serverSocketChannel = null;

    SocketChannel socketChannel = null;
    String receiveText;
    String sendText;
    int count;
    //測試此鍵的通道是否準備好接受新的套接字連接
    if(selectionKey.isAcceptable()){

    //返回創建此鍵的通道
    serverSocketChannel = (ServerSocketChannel)selectionKey.channel();

    //接受客戶端建立連接的請求,并返回 SocketChannel 對象
    socketChannel = serverSocketChannel.accept();

    //配置為非阻塞
    socketChannel.configureBlocking(false);

    //注冊到selector
    socketChannel.register(selector, SelectionKey.OP_READ);

    }else if(selectionKey.isReadable()){
    //返回為之創建此鍵的通道
    socketChannel = (SocketChannel)selectionKey.channel();

    //將緩沖區清空,以備下次讀取
    receiveBuffer.clear();

    //將發送來的數據讀取到緩沖區

    count = socketChannel.read(receiveBuffer);


    if(count>0){
    receiveText = new String(receiveBuffer.array(),0,count);
    System.out.println("服務器端接受到的數據---"+receiveText);
    socketChannel.register(selector, SelectionKey.OP_WRITE);
    }
    }else if (selectionKey.isWritable()) {
    //將緩沖區清空以備下次寫入
    sendBuffer.clear();

    // 返回為之創建此鍵的通道。
    socketChannel = (SocketChannel) selectionKey.channel();

    sendText="message from server--" + flag++;
    //向緩沖區中輸入數據
    sendBuffer.put(sendText.getBytes());

    //將緩沖區各標志復位,因為向里面put了數據標志被改變要想從中讀取數據發向服務器,就要復位
    sendBuffer.flip();

    //輸出到通道
    socketChannel.write(sendBuffer);

    System.out.println("服務器端向客戶端發送數據--:"+sendText);
    socketChannel.register(selector, SelectionKey.OP_READ);
    }

    }
    public static void main(String[] args) throws IOException {
    int port = 8888;
    NIOServer server = new NIOServer(port);
    server.listen();
    }

    }
    復制代碼

    客戶端

    復制代碼
    package com.eric.test.nio;

    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Set;

    public class NIOClient {
    /*標識數字*/
    private static int flag = 0;
    /*緩沖區大小*/
    private static int BLOCK = 4096;
    /*接受數據緩沖區*/
    private static ByteBuffer sendBuffer = ByteBuffer.allocate(BLOCK);
    /*發送數據緩沖區*/
    private static ByteBuffer receiveBuffer = ByteBuffer.allocate(BLOCK);
    /*服務器端地址*/
    private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
    "localhost", 8888);

    public static void main(String[] args) throws IOException {
    // 打開socket通道
    SocketChannel clientChannel = SocketChannel.open();

    // 設置為非阻塞方式
    clientChannel.configureBlocking(false);

    // 打開選擇器
    Selector selector = Selector.open();

    // 注冊連接服務端socket動作
    clientChannel.register(selector, SelectionKey.OP_CONNECT);

    // 連接
    clientChannel.connect(SERVER_ADDRESS);


    SocketChannel socketChannel;
    Set<SelectionKey> selectionKeys;
    String receiveText;
    String sendText;
    int count=0;

    while (true) {
    //選擇一組鍵,其相應的通道已為 I/O 操作準備就緒。
    //監控所有注冊的 channel ,當其中有注冊的 IO 操作可以進行時,該函數返回,并將對應的 SelectionKey 加入 selected-key set
    selector.select();

    //返回此選擇器的已選擇鍵集。
    selectionKeys = selector.selectedKeys();

    //System.out.println(selectionKeys.size());
    for(SelectionKey selectionKey:selectionKeys){

    //判斷是否為建立連接的事件
    if (selectionKey.isConnectable()) {

    System.out.println("client connect");
    socketChannel = (SocketChannel) selectionKey.channel(); //
    // 判斷此通道上是否正在進行連接操作。
    // 完成套接字通道的連接過程。
    if (socketChannel.isConnectionPending()) {

    //完成連接的建立(TCP三次握手)
    socketChannel.finishConnect();

    System.out.println("完成連接!");
    sendBuffer.clear();
    sendBuffer.put("Hello,Server".getBytes());
    sendBuffer.flip();
    socketChannel.write(sendBuffer);
    }
    socketChannel.register(selector, SelectionKey.OP_READ);
    } else if (selectionKey.isReadable()) {
    socketChannel = (SocketChannel) selectionKey.channel();
    //將緩沖區清空以備下次讀取
    receiveBuffer.clear();

    //讀取服務器發送來的數據到緩沖區中
    count=socketChannel.read(receiveBuffer);

    if(count>0){
    receiveText = new String( receiveBuffer.array(),0,count);
    System.out.println("客戶端接受服務器端數據--:"+receiveText);
    socketChannel.register(selector, SelectionKey.OP_WRITE);
    }

    } else if (selectionKey.isWritable()) {
    sendBuffer.clear();
    socketChannel = (SocketChannel) selectionKey.channel();
    sendText = "message from client--" + (flag++);
    sendBuffer.put(sendText.getBytes());
    //將緩沖區各標志復位,因為向里面put了數據標志被改變要想從中讀取數據發向服務器,就要復位
    sendBuffer.flip();

    socketChannel.write(sendBuffer);
    System.out.println("客戶端向服務器端發送數據--:"+sendText);
    socketChannel.register(selector, SelectionKey.OP_READ);
    }
    }
    selectionKeys.clear();
    }
    }
    }
    復制代碼

    小結:之前對Selector注冊事件和SocketChannel有點小困惑。SocketChannel就像一根水管,當監聽到寫事件時,就往管道寫數據;當監聽到讀事件時,就從管道讀出數據。

    posted @ 2012-04-25 15:34 Daniel 閱讀(442) | 評論 (1)編輯 收藏
      2011年12月21日

    I、關系數據庫設計范式介紹

    1.1 第一范式(1NF)無重復的列

     
          所謂第一范式(1NF)是指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重復的屬性。如果出現重復的屬性,就可能需要定義一個新的實體,新的實體由重復的屬性構成,新實體與原實體之間為一對多關系。在第一范式(1NF)中表的每一行只包含一個實例的信息。簡而言之,第一范式就是無重復的列。

    說明:在任何一個關系數據庫中,第一范式(1NF)是對關系模式的基本要求,不滿足第一范式(1NF)的數據庫就不是關系數據庫。 

    1.2 第二范式(2NF)屬性完全依賴于主鍵[消除部分子函數依賴]

     
          第二范式(2NF)是在第一范式(1NF)的基礎上建立起來的,即滿足第二范式(2NF)必須先滿足第一范式(1NF)。第二范式(2NF)要求數據庫表中的每個實例或行必須可以被惟一地區分。為實現區分通常需要為表加上一個列,以存儲各個實例的惟一標識。例如員工信息表中加上了員工編號(emp_id)列,因為每個員工的員工編號是惟一的,因此每個員工可以被惟一區分。這個惟一屬性列被稱為主關鍵字或主鍵、主碼。 
            第二范式(2NF)要求實體的屬性完全依賴于主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性,如果存在,那么這個屬性和主關鍵字的這一部分應該分離出來形成一個新的實體,新實體與原實體之間是一對多的關系。為實現區分通常需要為表加上一個列,以存儲各個實例的惟一標識。簡而言之,第二范式就是屬性完全依賴于主鍵。 

    1.3 第三范式(3NF)屬性不依賴于其它非主屬性[消除傳遞依賴]


                滿足第三范式(3NF)必須先滿足第二范式(2NF)。簡而言之,第三范式(3NF)要求一個數據庫表中不包含已在其它表中已包含的非主關鍵字信息。例如,存在一個部門信息表,其中每個部門有部門編號(dept_id)、部門名稱、部門簡介等信息。那么在的員工信息表中列出部門編號后就不能再將部門名稱、部門簡介等與部門有關的信息再加入員工信息表中。如果不存在部門信息表,則根據第三范式(3NF)也應該構建它,否則就會有大量的數據冗余。簡而言之,第三范式就是屬性不依賴于其它非主屬性。 

    II、范式應用實例剖析


            下面以一個學校的學生系統為例分析說明,這幾個范式的應用。首先第一范式(1NF):數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。在當前的任何關系數據庫管理系統(DBMS)中,傻瓜也不可能做出不符合第一范式的數據庫,因為這些DBMS不允許你把數據庫表的一列再分成二列或多列。因此,你想在現有的DBMS中設計出不符合第一范式的數據庫都是不可能的。 
    首先我們確定一下要設計的內容包括那些。學號、學生姓名、年齡、性別、課程、課程學分、系別、學科成績,系辦地址、系辦電話等信息。為了簡單我們暫時只考慮這些字段信息。我們對于這些信息,說關心的問題有如下幾個方面。 
    • 學生有那些基本信息
    • 學生選了那些課,成績是什么
    • 每個課的學分是多少
    • 學生屬于那個系,系的基本信息是什么。

    2.1 第二范式(2NF)實例分析

     
          首先我們考慮,把所有這些信息放到一個表中(學號,學生姓名、年齡、性別、課程、課程學分、系別、學科成績,系辦地址、系辦電話)下面存在如下的依賴關系。 
            (學號)→ (姓名, 年齡,性別,系別,系辦地址、系辦電話) 
             (課程名稱) → (學分) 
            (學號,課程)→ (學科成績)
    2.1.1 問題分析
     
          因此不滿足第二范式的要求,會產生如下問題 

            數據冗余: 同一門課程由n個學生選修,"學分"就重復n-1次;同一個學生選修了m門課程,姓名和年齡就重復了m-1次。 

            更新異常: 
                 1)若調整了某門課程的學分,數據表中所有行的"學分"值都要更新,否則會出現同一門課程學分不同的情況。 
                2)假設要開設一門新的課程,暫時還沒有人選修。這樣,由于還沒有"學號"關鍵字,課程名稱和學分也無法記錄入數據庫。 

           刪除異常 : 假設一批學生已經完成課程的選修,這些選修記錄就應該從數據庫表中刪除。但是,與此同時,課程名稱和學分信息也被刪除了。很顯然,這也會導致插入異常。
    2.1.2 解決方案
     
          把選課關系表SelectCourse改為如下三個表:
    • 學生:Student(學號,姓名, 年齡,性別,系別,系辦地址、系辦電話);
    • 課程:Course(課程名稱, 學分);
    • 選課關系:SelectCourse(學號, 課程名稱, 成績)。

    2.2 第三范式(3NF)實例分析


            接著看上面的學生表Student(學號,姓名, 年齡,性別,系別,系辦地址、系辦電話),關鍵字為單一關鍵字"學號",因為存在如下決定關系: 

           (學號)→ (姓名, 年齡,性別,系別,系辦地址、系辦電話) 
            但是還存在下面的決定關系 
           (學號) → (所在學院)→(學院地點, 學院電話) 
            即存在非關鍵字段"學院地點"、"學院電話"對關鍵字段"學號"的傳遞函數依賴。 
            它也會存在數據冗余、更新異常、插入異常和刪除異常的情況。 (數據的更新,刪除異常這里就不分析了,可以參照2.1.1進行分析)

            根據第三范式把學生關系表分為如下兩個表就可以滿足第三范式了: 

            學生:(學號, 姓名, 年齡, 性別,系別); 
            系別:(系別, 系辦地址、系辦電話)。 

    總結


           上面的數據庫表就是符合I,II,III范式的,消除了數據冗余、更新異常、插入異常和刪除異常。 
    posted @ 2011-12-21 16:46 Daniel 閱讀(217) | 評論 (0)編輯 收藏
      2011年12月1日
    <html>   
      <head>   
      <meta http-equiv="Content-Type" content="text/html; charset=gb2312">   
      <meta name="GENERATOR" content="Microsoft FrontPage 4.0">   
      <meta name="ProgId" content="FrontPage.Editor.Document">   
      <title>光標位置</title>   
      <style>   
      INPUT{border: 1 solid #000000}   
      BODY,TABLE{font-size: 10pt}   
      </style>   
      </head>   
      <body>   
      <table border="0" width="700" cellspacing="0" cellpadding="0">   
      <tr>   
      <td width="479" rowspan="17">   
      點擊 TextArea 實現光標定位   
      <p>   
      <input type="Button" value="ff" onclick="fo()"/>
      <input type="Button" value="UPLINE" onclick="upLine()"/>
      <input type="Button" value="DOWNLINE" onclick="downLine()"/>
      <textarea rows="17" cols="49" id="box" onclick="tellPoint()" >
    HIGHHEEL
    sfsfsfs
    s1123
    432434
    asf
    fsa
    sf
    3232</textarea> 
      <script type="text/javascript">
      function getPos(textarea) {
      var rangeData = {text: "", start: 0, end: 0 };
     
      if (textarea.setSelectionRange) { // W3C 
       textarea.focus();
       rangeData.start= textarea.selectionStart;
       rangeData.end = textarea.selectionEnd;
       rangeData.text = (rangeData.start != rangeData.end) ? textarea.value.substring(rangeData.start, rangeData.end): "";
      } else if (document.selection) { // IE
       textarea.focus();
       var i,
        oS = document.selection.createRange(),
        // Don't: oR = textarea.createTextRange()
        oR = document.body.createTextRange();
       oR.moveToElementText(textarea);
       
       rangeData.text = oS.text;
       rangeData.bookmark = oS.getBookmark();
       
       // object.moveStart(sUnit [, iCount]) 
       // Return Value: Integer that returns the number of units moved.
       for (i = 0; oR.compareEndPoints('StartToStart', oS) < 0 && oS.moveStart("character", -1) !== 0; i ++) {
        // Why? You can alert(textarea.value.length)
        if (textarea.value.charAt(i) == '/r' ) {
         i ++;
        }
       }
       rangeData.start = i;
       rangeData.end = rangeData.text.length + rangeData.start;
      }
      
      return rangeData;
      }
      
      function fo(){
     var text = new String(document.getElementById("box").value);
     var currentCurrosr = document.getElementById("pnum").value;
     alert(text.substring(currentCurrosr-1, currentCurrosr));
     pos = getPos(document.getElementById("box"));
     alert(pos.start);
    //       var end = text.indexOf("\r\n", document.getElementById("pnum").value);
    //      if(end == -1){
    //     alert("END");
    //      }
    //     var start = text.lastIndexOf("\r\n", currentCurrosr);
    //     if(start == -1){alert("START");}
    //     var currentLine = getLine(currentCurrosr);
    //     alert("CUL"+currentLine+"L"+currentLine.length);
    //     var nextLine = getLine(end + 1);
    //     alert("NEXT"+nextLine+"L"+nextLine.length);
    //     var preLine = getLine(start - 1);
    //     alert("PRE"+ preLine+"L"+preLine.length);
      }
      
      function upLine(){
     var text = new String(document.getElementById("box").value);
     var currentCurrosr = document.getElementById("h1").value;
          var end = text.indexOf("\r\n",currentCurrosr);
          var start = null;
          if(currentCurrosr == end){
    start =text.lastIndexOf("\r\n", currentCurrosr-1);  
          }else{
          start = text.lastIndexOf("\r\n", currentCurrosr);
          }
          if(start == -1){alert("Already the toppest!");
          return;
          }
          var preLine = getLine(start-1);
          var currentLine = getLine(currentCurrosr);
          var preLineStart = text.lastIndexOf("\r\n", start-1);
          var sq = null;
          var endPart = text.substring(end,text.length);
        alert(text.length);
          if(preLineStart == -1){
        sq = new String(currentLine).substring(2, currentLine.length).concat("\r\n").concat(preLine).concat(endPart);
        alert(sq);
        alert(sq.length);
          }else{
         alert(preLineStart);
         sq = text.substring(0, preLineStart).concat(currentLine).concat(preLine).concat(endPart);
              alert(sq);
              alert(sq.length);
          }
          document.getElementById("box").value=sq;
      }
      
      function downLine(){
     
      }
      
       
      
      
      function getLine(currsor){
    var text = new String(document.getElementById("box").value);
    var end = text.indexOf("\r\n", currsor);
    if(end == -1){
    return text.substring(text.lastIndexOf("\r\n", text.length), text.length);
    }
    var tmp = text.substring(0, end);
    var start = tmp.lastIndexOf("\r\n", end);
    if(start == -1){
    }
    // alert(tmp);
    // alert(start);
    // alert(text.substring(start+4, end).length);
    // alert(text.substring(start+4, end));
    return text.substring(start, end);
    }
      function movePoint()   
      {   
      var pn=parseInt(pnum.value);   
      if(isNaN(pn))   
      return;   
      var rng=box.createTextRange();   
      rng.moveStart("character",pn);   
      rng.collapse(true);   
      rng.select();   
      returnCase(rng)   
      }   
      function tellPoint()   
      { 
      var rng=event.srcElement.createTextRange();   
      rng.moveToPoint(event.x,event.y);   
      rng.moveStart("character",-event.srcElement.value.length)   
      h1.value=rng.text.length; 
      pnum.value=rng.text.length;
      returnCase(rng)   
      }   
       
      function returnCase(rng)   
      {   
      bh.innerText=rng.boundingHeight;   
      bl.innerText=rng.boundingLeft;   
      bt.innerText=rng.boundingTop;   
      bw.innerText=rng.boundingWidth;   
      ot.innerText=rng.offsetTop;   
      ol.innerText=rng.offsetLeft;   
      t.innerText=rng.text;   
      }   
       
      function selectText(sp,ep)   
      {   
      sp=parseInt(sp)   
      ep=parseInt(ep)   
      if(isNaN(sp)||isNaN(ep))   
      return;   
      var rng=box.createTextRange();   
      rng.moveEnd("character",-box.value.length)   
      rng.moveStart("character",-box.value.length)   
      rng.collapse(true);   
      rng.moveEnd("character",ep)   
      rng.moveStart("character",sp)   
      rng.select();   
      returnCase(rng);   
      }   
      var rg=box.createTextRange();   
      function findText(tw)   
      {   
      if(tw=="")   
      return;   
      var sw=0;   
      if(document.selection)   
      {   
      sw=document.selection.createRange().text.length;   
      }   
      rg.moveEnd("character",box.value.length);   
      rg.moveStart("character",sw);   
       
      if(rg.findText(tw))   
      {   
      rg.select();   
      returnCase(rg);   
      }   
      if(rg.text!=tw)   
      {   
      alert("已經搜索完了");   
      rg=box.createTextRange();   
      }   
      }   
      </script>   
      </p>   
      <p></p>
      
      <input type="hidden" id="h1">   
      光標位置:<input type="text" value="0" id="pnum" size="8"> <input type="button" onclick="movePoint()" value="移動光標到指定位置">   
      <p></p>   
      選擇指定范圍:<input type="text" size="9" id="sbox"> -- <input type="text" size="9" id="ebox"> <input type="button" onclick="selectText(sbox.value,ebox.value)" value="選擇">   
      <p></p>   
      選擇查找字符 :<input type="text" value="" id="cbox" size="8"> <input type="button" onclick="findText(cbox.value)" value="查找下一個并選擇">   
      </td>   
      <td width="217">boundingHeight:&nbsp;<span id="bh"></span></td>   
      </tr>   
      <tr>   
      <td width="217">boundingWidth:&nbsp;<span id="bw"></span></td>   
      </tr>   
      <tr>   
      <td width="217">boundingTop:&nbsp;<span id="bt"></span></td>   
      </tr>   
      <tr>   
      <td width="217">boundingLeft:&nbsp;<span id="bl"></span></td>   
      </tr>   
      <tr>   
      <td width="217">offsetLeft:&nbsp;<span id="ol"></span> </td>   
      </tr>   
      <tr>   
      <td width="217">offsetTop:&nbsp;<span id="ot"></span> </td>   
      </tr>   
      <tr>   
      <td width="217">text:&nbsp;<span style="position: absolute; z-index: 10" id="t" ></span> </td>   
      </tr>   
      </table>   
      </body>   
      </html>
    posted @ 2011-12-01 23:03 Daniel 閱讀(303) | 評論 (0)編輯 收藏
      2011年8月31日


    14.1 Web Services和面向服務的軟件架構(Service Oriented Architecture,簡稱SOA)概述:

     

    在最新Java開發世界里,我們經常會遇到這樣一個名詞:Web Services(Web服務)。同時還會發現,與這個名詞同時出現的多是各大主流技術供應商,各大技術供應商無一不在關注這一領域的發展。從Microsoft的.NET架構,到SUN的SUN ONE,以及IBM的Web Services,都體現了這些重量級的技術提供者對Web Services的推崇與重視。

     

    電子商務的發展促進了Web Services的發展。Web服務可以使公司降低進行電子商務的成本,更快地部署解決方案以及開拓更多的新機遇。Web服務使應用程序的集成比以前更快、更容易而且更便宜。它更注重服務語義而不那么注重網絡協議語義的消息,從而實現了業務功能的松散耦合。這些特性對于在企業之間和企業內部通過web連接業務功能是非常理想的。它提供了一致化(Uniform)的編程模型,從而在企業內外都可以利用通用的基礎設施并以一種通用的方法進行應用程序集成。

     

    要理解Web Services, 首先需要認識面向服務的軟件架構(Service Oriented Architecture,簡稱SOA),Web Services是SOA架構系統的一個實例。

    14.1.1面向服務的軟件架構(SOA)

     

    1. 面向服務中的基本概念

    在面向服務的架構中包含一些基本的概念,透過這些基本概念可以進一步了解面向服務的架構。

    (1) 服務的概念

    在SOA中的服務是指能夠處理一些任務過程的動作的抽象概念,這些服務可以被描述,可以被發現,可以由服務代理負責向請求者提供服務并給出結果。代理是指請求或者提供服務的人所使用的軟件工具,人通過代理進行交互操作。

    (2) 消息的概念

    服務代理之間需要通過消息的傳遞進行交互操作,消息的內容含有一定的語義和數據,消息傳輸需要與某個通信協議相綁定才能夠實現。

    (3) 服務的描述和發現

    眾多的服務組成一個開放系統,除了需要提供信息交互方式以外,還需要提供相互了解的機制,這就需要提供描述和發現的方式。代理可以通過服務的描述來了解一個服務的內容,包括使用這個服務的技術信息、訪問這個服務的地址信息等內容。當新的服務被投入到系統之中后,它需要被注冊,并且要能夠被發現,使它可以被利用起來。

     

    2.為什么需要面向服務的軟件

    由于軟件需求的擴大,軟件系統變得越來越復雜。面對復雜的系統資源,我們需要一種更加合理的方式將不同類型、不同位置的子系統有力地結合起來,這種整合并不是將它們之間綁定得更加緊密,而是利用更加松散的方式來建立這個系統。

    SOA通過松散的方式將分布式的軟件模塊結合起來,與舊有系統集成相比有著明顯的優勢。對于服務的使用者來說,可以簡單地通過服務的描述來獲取服務,系統各部分之間不必為了某一部分的升級而改變,在服務的過程中不同的軟件模塊可以充當不同的角色,從而構成整個系統的工作體系。

    在SOA當中,一個服務代表的是一個由服務提供者向服務請求者發布的一些處理過程,這個過程在被調用之后,獲得服務請求者所需要的一個結果。在這個過程中,服務請求者可以向任何能夠提供此項服務的服務提供者來請求服務,服務實現的過程對于服務請求者來說是透明的。

    隨著系統分布式和多種結構復合程度的提高,SOA的巨大優勢將進一步被挖掘。

    (1) 建立松散耦合的系統

    松散耦合系統的優點已經被業內充分地認可,SOA作為一種分布式的系統,它實現了一種服務和描述等概念相結合的架構。

    SOA中的服務在SOA架構中被標準的描述語言所描述,并通過與某種傳輸協議的綁定來實現相互之間的交互。這種基于服務的架構使整個系統成為一個松散耦合的結構,利用與通信協議的綁定將分布式系統中的所有部分連接起來,利用語義和服務的描述,在代理之間進行交互。

    (2) 提高軟件的重用性

    面向服務的架構還提高了對軟件的重用性。與組件方式相比,SOA系統中的單個服務的改變不會對其他部分造成嚴重的影響,同時利用服務的描述使同一服務可以充分地被其他系統所調用,各個系統之間形成了高度的重用性。

    現在正在被廣泛使用的一些服務,正在不斷地被各個系統所重用,重用的條件十分簡單,只要了解服務的描述,或者可以訪問到服務的描述地址即可。與組件重用相比,服務的重用還具有與實現語言無關的特點,重用服務的客戶端程序不需要使用與服務實現部分同樣的開發語言,一切交互的過程都是利用與實現無關的方式進行的。

    (3) 提供按需服務的代碼

    面向服務的架構也使得系統的實現虛擬化,在SOA架構中的請求和提供之間交互或相互代理的過程中,可計算的代碼資源分布在松散結構中的各個部分上,當請求發生時才被調用和服務,所有計算過程都是按照請求者的需求進行的。服務的對象分為有狀態和無狀態兩種方式提供服務,按照需求提供服務,也可以利用緩沖機制優化系統的系統。

    總之,SOA使代碼的開發變得更有服務的目的性,使開發更加有效和合理。

     

    14.1.2 SOA與 Web 2.0

    另外,我們補充一下SOA與目前同樣熱門的Web 2.0的關系。

    實際上Web 2.0 和SOA的概念在很大程度上是相同的,只是被粉飾成為軟件的不同部分(如果的確存在不同的話),也就是說SOA和Web 2.0有很多重疊的東西,例如都是基于調用(invoke)的服務,都能存在于網絡的任何位置等等。

    SOA和Web 2.0的共性遠大于它們之間的區別,而且Web 2.0在推廣SOA方面起到了一定作用。到現在為止,SOA和Web 2.0擁有不同的支持者- SOA更多涉及企業結構和商務開拓,而Web 2.0更關注用戶。這種差別隨著更多企業接納Web 2.0而在變化,但是這兩項技術有著不同的重心: Web 2.0告訴我們數據是軟件應用中最重要的部分,而SOA告訴我們服務才是中心。SOA中傳輸數據的服務也非常重要,但是傳統SOA更關注IT系統的接合處而不是那些能使接合處更具價值的東西。也就是說,SOA也許是通暢的管道,但并不是系統中通過的水的價值。許多行業領導者說企業同時需要SOA類方法的結構和Web 2.0方法的創業能力。

    SOA和Web 2.0之間有許多共有的要素:

    l 軟件重組

    l 管理

    l 軟件就是服務

    l 應用就是平臺

    l 無意識的使用

    l 開放

    l AJAX

    l 互操作性

    l 貨幣化

    l 安全

    l 網絡導向架構

    特別要說的是,最后一條網絡導向架構或者Web Oriented Architecture(WOA)是關鍵的內容,最終有可能會將SOA和Web 2.0合為一體。

     

    了解了SOA后, 我們來介紹什么是Web Services。Web Services是SOA架構系統的一個實例。從技術角度來講,Web Services是一種新的技術架構、新的軟件應用環境。它的系統架構和實現技術完全繼承已有的技術,可以認為Web Services是Internet的一種延伸,是現有的Internet面向更好的互操作能力的一個延伸。

    14.2. Web Services的概念

    Web Services,從字面上理解就是通過Web提供的服務。我們可以理解Web Services是自包含的、模塊化的應用程序,它可以在網絡(通常為Web)中被描述、發布、查找以及調用;也可以理解Web Services是基于網絡的、分布式的模塊化組件,它執行特定的任務,遵守具體的技術規范,這些規范使得Web Sevices能與其他兼容的組件進行互操作;也可以這樣理解,所謂Web服務,它是指由企業發布的完成其特別商務需求的在線應用服務,其他公司或應用軟件能夠通過Internet來訪問并使用這項應用服務

    對于Web Services,很多人會與Web Service混為一談,認為二者指的是同一個事物。其實不然,前者指的是用于建構Web Service的技術框架,后者指的是使用Web Services技術而創建的應用實例。Web Services是描述了一些操作的接口,基于標準化的XML消息傳輸機制,我們可以通過網絡訪問這些操作。Web Services使用規范的、基于XML的WSDL(Web Services Description Language)語言描述的,這稱為Web Services的服務描述。這一描述囊括了與服務交互所需要的全部細節,包括消息格式(詳細描述操作的輸入輸出消息格式)、傳輸協議和位置。該接口隱藏了服務實現的細節,允許通過獨立與服務實現、獨立于軟硬件平臺、獨立于編寫服務所用的編程語言的方式使用該服務。這使得基于Web Services的應用程序具有松散耦合、面向組件和跨技術實現的特點。Web Services都履行一定的特定業務或任務,可以實現同其他Web Services一起用于實現復雜的商業交易。

    從外部使用者角度而言,Web Services是一種部署在Web上的對象和組件,具備以下特征:

    .完好的封裝性:

    Web服務既然是一種部署在web上的對象,自然具備對象的良好封裝性,對于使用者而言,他能且僅能看到該對象提供的功能列表。

    .松散耦合

    這一特征也是源于對象/組件技術,當一個Web服務的實現發生變更的時候,調用者是不會感到這一點的,對于調用者來說,只要Web服務的調用界面不變,Web服務實現的任何變更對他們來說都是透明的,甚至是當Web服務的實現平臺從J2EE遷移到了.NET或者是相反的遷移流程,用戶都可以對此一無所知。對于松散耦合而言,尤其是在Internet環境下的Web服務而言,需要有一種適合Internet環境的消息交換協議,而XML/SOAP正是目前最為適合的消息交換協議。

    .使用協議的規范性

    這一特征從對象而來,但相比一般對象,它更加規范化和易于理解。首先,作為Web服務,對象界面所提供的功能應當使用標準的描述語言來描述(比如WSDL);其次,由標準描述語言描述的服務界面應當是能夠被發現的,因此這一描述文檔需要被存儲在私有的或公共的注冊庫里面。同時,使用標準描述語言描述的使用協約將不僅僅是服務界面,它將被延伸到Web服務的聚合、跨Web服務的事務、工作流等,而這些又都需要服務質量(QoS)的保障。其次,我們知道安全機制對于松散耦合的對象環境的重要性,因此我們需要對諸如授權認證、數據完整性(比如簽名機制)、消息源認證以及事務的不可否認性等運用規范的方法來描述、傳輸和交換。最后,在所有層次的處理都應當是可管理的,因此需要對管理協約運用同樣的機制。

    .高度可集成能力

    由于Web服務采取簡單的、易理解的標準,Web協議作為組件界面描述和協同描述規范,完全屏蔽了不同軟件平臺的差異,無論是CORBA、DCOM還是EJB,都可以通過這一種標準的協議進行互操作,實現了在當前環境下最高的可集成性。

     

    14.2.1 Web Services的核心技術

     

    Web Services 是一種基于組件的軟件平臺,是面向服務的Internet 應用。Web Services 是應用于Internet 的,而不是限于局域網或試驗環境,這就要求Web Services 框架必須適用于現有的Internet 軟件和硬件環境,即服務的提供者所提供的服務必須具有跨平臺、跨語言的特性。其次,Web Services 所提供的服務不但是面向人,而且需服務于其它應用系統。現有的Web網站也可以認為是面向服務的,但這種服務僅僅可以提供給人使用(只有人類才可以讀懂瀏覽器下載的頁面) 。而新一代的Web Services 所提供的服務應能被機器所讀懂,例如其它應用程序及移動設備中的軟件系統。這樣,我們可以看出,Web Services 的發展方向實際上是構造一個基于現有Internet 技術之上的分布計算系統。

    Web Services 框架的核心技術包括SOAP(Simple Object Access Protocol,簡單對象訪問協議) ,WSDL(Web Services Description Lanuage,Web服務描述語言) 和UDDI(Universal Description,Discovery and Integration,通用描述,發現,集成) ,它們都是以標準的XML 文檔的形式表述的。

    XML是Web Services技術體系中最基礎的標準,Web Services的一切都建立在XML技術的基礎之上,包括Web Services的消息、描述和服務實現的各個環節。利用XML,Web Services的服務提供者和請求者可以利用不同的開發語言來協作完成服務調用的過程。XML是Web Services技術體系中的很多標準得以建立的基礎,在Web Services系統中無處不在。

     

    SOAP 是Web services 的通信協議。SOAP是一種簡單的、輕量級的基于XML 的機制,用于在網絡應用程序之間進行結構化數據交換。SOAP包括三部分:一個定義描述消息內容的框架的信封,一組表示應用程序定義的數據類型實例的編碼規則,以及表示遠程過程調用和響應的約定。

    WSDL表示WEB服務說明語言。WSDL文件是一個XML 文檔,用于說明一組SOAP消息以及如何交換這些消息,通過WSDL可以描述一個服務的信息。這些信息使不了解這個服務的開發者可以建立調用這個服務的客戶端代碼,或者通過WSDL幫助生成實現它的基本代碼結構。WSDL在Web Services的實際開發過程中起著重要的作用。

     

      Web Services是基于互聯網的應用程序模塊,用于在互聯網上運行,它采用開放的UDDI(Universal Description,Discovery and Integration,通用描述,發現,集成)標準。UDDI標準先由IBM、微軟、Ariba制訂,到目前為止獲得了130多家公司的支持。UDDI 提供一種發布和查找服務描述的方法。UDDI 數據實體提供對定義業務和服務信息的支持。WSDL 中定義的服務描述信息是UDDI注冊中心信息的補充。UDDI提供了一個開放,平臺獨立的技術框架,來使企業之間能在互聯網上找到對方的服務,定義它們在互聯網上的交互活動,以及這些信息的共享方式。

     

    Web 服務體系結構基于三種角色(服務提供者、服務注冊中心和服務請求者)之間的交互。

    服務提供者。從企業的角度看,這是服務的所有者。從體系結構的角度看,這是托管訪問服務的平臺。 
    服務請求者(用戶)。從企業的角度看,這是要求滿足特定功能的企業。從體系結構的角度看,這是尋找并調用服務,或啟動與服務的交互的應用程序。服務請求者角色可以由瀏覽器來擔當,由人或無用戶界面的程序(例如,另外一個 Web 服務)來控制它。 
    服務注冊中心。這是可搜索的服務描述注冊中心,服務提供者在此發布他們的服務描述。在靜態綁定開發或動態綁定執行期間,服務請求者查找服務并獲得服務的綁定信息(在服務描述中)。對于靜態綁定的服務請求者,服務注冊中心是體系結構中的可選角色,因為服務提供者可以把描述直接發送給服務請求者。同樣,服務請求者可以從服務注冊中心以外的其它來源得到服務描述,例如本地文件、FTP 站點、Web 站點、廣告和服務發現(Advertisement and Discovery of Services,ADS)或發現 Web 服務(Discovery of Web Services,DISCO)。 

      Web Services 的體系架構如圖1 所示

    Web Services 服務提供方通過WSDL(Web Services Description Language) 描述所提供的服務,并將這一描述告知Web Services 注冊服務器。注冊服務器依據WSDL 的描述,依照UDDI (Universal Description Discovery and Integration) 的協定更新服務目錄并在Internet 上發布。用戶在使用Web Services 前先向注冊服務器發出請求,獲得Web Services 提供者的地址和服務接口信息,之后使用SOAP 協議(Simple Object Access Protocol) 與Web Services 提供者建立連接,進行通信。Web Services 的技術主要建立在XML 的規范之上,這保證了這一體系結構的平臺無關性、語言無關性和人機交互性能。

     

    14.2.2 Web 服務開發生命周期

    Web 服務開發生命周期包括了設計和部署以及在運行時對服務注冊中心、服務提供者和服務請求者每一個角色的要求。每個角色對開發生命周期的每一元素都有特定要求。

    Web 服務開發生命周期有以下四個階段:

    1. 構建 
    生命周期的構建階段包括開發和測試 Web 服務實現、定義服務接口描述和定義服務實現描述。我們可以通過創建新的 Web 服務、把現有的應用程序變成 Web 服務和由其它 Web 服務和應用程序組成新的 Web 服務來提供 Web 服務的實現。

    2. 部署 
    部署階段包括向服務請求者或服務注冊中心發布服務接口和服務實現的定義,以及把 Web 服務的可執行文件部署到執行環境(典型情況下,Web 應用程序服務器)中。

    3. 運行 
    在運行階段,可以調用 Web 服務。在此,Web 服務完成部署,成為可操作的服務。服務請求者可以進行查找和綁定操作。

    4. 管理 
    管理階段包括持續的管理和經營 Web 服務應用程序。安全性、可用性、性能、服務質量和業務流程問題都必須被解決。

     

    接下來我們具體展開Web Services原理。

     

    14.3.Web Services原理

     

    首先,我們將看看 Web 服務的一個概念性協議棧以及這個協議棧的細節。然后我們將討論選擇網絡協議的標準。我們還將回顧一下基本的基于 XML 的消息傳遞分布式計算。我們將用服務描述擴展基本的 XML 消息傳遞,而服務描述是根據它的協議棧來解釋的。接下來,我們將討論服務描述在 Web 服務體系結構中的角色,說明支持靜態和動態 Web 服務應用程序的服務發布技術的范圍。我們還將圍繞服務發布討論服務發現的角色。最后,我們將描述基本 Web 服務體系結構的擴展,電子商務需要這些擴展才能使用 Web 服務。

     

    14.3.1 Web 服務協議棧

    要以一種可交互操作的方式執行發布、發現和綁定這三個操作,必須有一個包含每一層標準的 Web 服務協議棧。圖 2 展示了一個概念性 Web 服務協議棧。上面的幾層建立在下面幾層提供的功能之上。垂直的條表示在協議棧中每一層必須滿足的需求。

    圖2. Web 服務概念性協議棧

    Web 服務協議棧的基礎是網絡層。Web 服務要被服務請求者調用,就必須是可以通過網絡訪問的。互聯網上可以公用的 Web 服務使用普遍適用的網絡協議。HTTP 憑借其普遍性,成為了互聯網可用的 Web 服務真正的標準網絡協議。Web 服務還可以支持其它互聯網協議,包括 SMTP 和 FTP。內部網域可以使用可靠消息傳遞和調用基礎結構,如 MQSeries 和 CORBA 等等。

     

    下一層是基于 XML 的消息傳遞,它表示使用 XML 作為消息傳遞協議的基礎。選擇 SOAP 作為 XML 消息傳遞協議有很多原因:

    它是使用 XML 傳送以文檔為中心的消息以及遠程過程調用的標準化封裝機制。 
    SOAP 很簡單;它基本上是一個用 XML 信封作為有效負載的 HTTP POST。 
    SOAP 比對 XML 簡單的 HTTP POST 更受青睞,因為它定義了一個標準機制,這個機制將正交擴展(orthogonal extension)合并為使用 SOAP 報頭和對操作或函數進行標準編碼的消息。 
    SOAP 消息支持 Web 服務體系結構中的發布、查找和綁定操作。 
    服務描述層實際上是描述文檔的一個協議棧。首先,WSDL 是基于 XML 的服務描述的真正標準。這是支持可交互操作的 Web 服務所需的最小標準服務描述。WSDL 定義了服務交互的接口和結構。要指定業務環境、服務質量和服務之間的關系,我們還需要另外的描述。WSDL 文檔可以由其它服務描述文檔來補充,從而描述 Web 服務的這些更高級的方面。例如,描述業務環境除了使用 WSDL 文檔,還要使用 UDDI 數據結構。Web 服務流程語言(Web Services Flow Language,WSFL)文檔中則描述了服務組成和流程。

    因為 Web 服務被定義為可以通過 SOAP 從網絡進行訪問,并由服務描述表示,所以該協議棧中的前三層需要提供或使用 Web 服務。最簡單的協議棧將包括網絡層的 HTTP、XML 消息傳遞層的 SOAP 協議以及服務描述層的 WSDL。所有企業間或公用 Web 服務都應該支持這種可交互操作的基礎協議棧。Web 服務,特別是企業內部或專用 Web 服務,能夠支持其它的網絡協議和分布式計算技術。該協議棧提供了互操作性,它使 Web 服務能夠利用現有的互聯網基礎結構。這將使進入普遍存在的環境的成本非常低。另外,靈活性并不會因為互操作性需求而有所降低,因為我們可以為選擇性和增值的技術提供另外的支持。例如,我們必須支持 HTTP 上的 SOAP,但也可以同時支持 MQ 上的 SOAP。

    協議棧的最下面三層確立了保證一致性和互操作性的技術,而它們上面的兩層,即服務發布和服務發現,可以用多種解決方案來實現。

    任何能夠讓服務請求者使用 WSDL 文檔的操作,不管它處于服務請求者生命周期的哪個階段,都符合服務發布的標準。該層中最簡單、最靜態的實例就是服務提供者直接向服務請求者發送 WSDL 文檔。這被稱為直接發布。電子郵件是直接發布的載體之一。直接發布對靜態綁定的應用程序來說很有用。另外,服務提供者還可以將描述服務的文檔發布到主機本地 WSDL 注冊中心、專用 UDDI 注冊中心或 UDDI 運營商節點。

    Web 服務如果沒有被發布就不能被發現,所以說服務發現依賴于服務發布。該層的各種發現機制和一組發布機制互相平行。任何允許服務請求者獲得對服務描述的訪問權,并在運行時使應用程序能夠使用該服務描述的機制都必須符合服務發現的標準。最簡單、最靜態的發現的實例是靜態發現,其中服務請求者從本地文件獲取 WSDL 文檔。這通常都是通過直接發布獲取的 WSDL 文檔,或者前面查找操作的結果。另外,也可以通過使用本地 WSDL 注冊中心、專用 UDDI 注冊中心或 UDDI 運營商節點在設計時或運行時發現服務。因為 Web 服務實現是一種軟件模塊,所以通過組建 Web 服務來產生 Web 服務是很自然的。Web 服務的組合可以扮演很多角色之一。企業內部的 Web 服務可能會相互合作,從而對外顯示出一個單獨的 Web 服務接口,或者,來自不同企業的 Web 服務可以相互合作,從而執行機器到機器、企業到企業的事務。另外,工作流程管理者還可以在參與業務流程的時侯調用每個 Web 服務。最上面一層,即服務流程,描述了如何執行服務到服務的通訊、合作以及流程。WSFL 用于描述這些交互。要使 Web 服務應用程序滿足當今電子商務的迫切需求,就必須提供企業級基礎結構,包括安全性、管理和服務質量。這幾個垂直條在協議棧的每一層都必須得到解決。每一層的解決方案可以彼此獨立。隨著 Web 服務范例的采用和發展,將會出現更多此類垂直條。

    該協議棧的最下面幾層表示基礎 Web 服務協議棧,它們相對于協議棧中上面幾層來說更成熟,也更標準。Web 服務的成熟和采用將會帶動協議棧中上面幾層和垂直條的開發和標準化。

    網絡層

    Web 服務協議棧的最底層是網絡層。該層可表示任意多個網絡協議:HTTP、FTP、SMTP、消息排隊(Message Queuing)、互聯網 ORB 間協議(Internet Inter ORB Protocol,IIOP)上的遠程方法調用(Remote Method Invocation,RMI)、電子郵件等等。在任何給定的情況下使用的網絡協議都依賴于應用程序需求。

    對于可以從互聯網訪問的 Web 服務,人們選擇網絡技術的時侯通常會傾向于選擇普遍部署的協議,如 HTTP。對于內部網中提供和使用的 Web 服務,使用另外的網絡技術也會被認同。我們可以根據其它需求選擇網絡技術,包括安全性、可用性、性能以及可靠性。這使得 Web 服務可以利用已有的功能更高級的聯網基礎結構和面向消息的中間件,如 MQSeries。在有多種網絡基礎結構的企業中,HTTP 可以用來在這些基礎結構之間搭建橋梁。

    Web 服務的好處之一在于,它為專用內部網和公用互聯網服務的開發和使用提供了統一的編程模型。所以,網絡技術的選擇對服務開發者來說是透明的。

    基于 XML 消息傳遞的分布式計算

    Web 服務體系結構最基礎的支柱是 XML 消息傳遞。當前 XML 消息傳遞的行業標準是 SOAP。IBM、Microsoft 以及其它企業都向 W3C 建議 SOAP 作為 XML 協議工作組(XML Protocol Working Group)的基礎。XML 協議將代替 SOAP 作為行業標準 XML 消息傳遞協議的位置。當 W3C 發布 XML 協議的草案標準時,Web 服務體系結構就會從 SOAP 遷移到 XML 協議。

    SOAP 是一種簡單的、輕量級的基于 XML 的機制,用于在網絡應用程序之間進行結構化數據交換。SOAP 包括三部分:一個定義描述消息內容的框架的信封、一組表示應用程序定義的數據類型實例的編碼規則,以及表示遠程過程調用(remote procedure calls,RPC)和響應的約定。SOAP 可以和各種網絡協議(如 HTTP、SMTP、FTP 和 IIOP 或 MQ 上的 RMI)相結合使用,或者用這些協議重新封裝后使用。

    雖然理解這個基礎很重要,但多數 Web 服務開發者不必直接處理這個基礎結構。大多數 Web 服務都會使用從 WSDL 生成的經過優化的特定于編程語言的綁定。當服務提供者和服務請求者都在類似的環境中執行時,這種優化可能尤為重要。

    圖 4 展示了 XML 消息傳遞(即 SOAP)和網絡協議如何組成Web 服務體系結構的基礎。

    圖 4. 使用 SOAP 的 XML 消息傳遞

    網絡節點在基于 XML 消息傳遞的分布式計算中扮演提供者和請求者角色的基本要求是構建、解析 SOAP 消息的能力(或兩者兼而有之),以及在網絡上通信的能力(接收、發送消息,或兩者)。

    通常,在 Web 應用程序服務器中運行的 SOAP 服務器將執行這些功能。另外,我們也可以使用在 API 中封裝這些功能的特定于編程語言的運行庫。應用程序與 SOAP 的集成可以通過使用四個基本步驟來實現:

    在圖 4 中,服務提供者的應用程序在(1)創建一條 SOAP 消息。這條 SOAP 消息是調用由服務提供者提供的 Web 服務操作的請求。消息主體中的 XML 文檔可以是一個 SOAP RPC 請求,也可以是一個服務描述中所描述的以文檔為中心的消息。服務請求者將此信息和服務提供者的網址一起提供給 SOAP 基礎結構(例如一個 SOAP 客戶機運行時)。SOAP 客戶機運行時與一個底層網絡協議(例如 HTTP)交互,然后在網絡上將 SOAP 消息發送出去。 
    網絡基礎結構在(2)將消息傳送到服務提供者的 SOAP 運行時(例如一個 SOAP 服務器)。SOAP 服務器將請求消息路由到服務提供者的 Web 服務。如果應用程序需要,SOAP 運行時負責將 XML 消息轉換為特定于編程語言的對象。這個轉換由消息中可以找到的編碼模式所控制。 
    Web 服務負責處理請求信息并生成一個響應。該響應也是一條 SOAP 消息。響應的 SOAP 消息在(3)被提供給 SOAP 運行時,其目的地是服務請求者。在 HTTP 上的同步請求/響應的情況中,聯網協議的底層請求/響應本質用于實現消息傳遞的請求/響應本質。SOAP 運行時將 SOAP 消息響應發送到網絡上的服務請求者。 
    響應消息在(4)由服務請求者節點上的聯網基礎結構接收。消息會經過整個 SOAP 基礎結構;可能會將 XML 消息轉換為目標編程語言中的對象。然后,響應消息被提供給應用程序。 
    本示例使用了請求/響應傳送基本原理,這種原理在大多數分布式計算環境中都很常見。請求/響應交換可以是同步的,也可以是異步的。其它傳送基本原理,如單向消息傳遞(無響應),通知(推動式響應)以及發布/訂閱,也可能用到 SOAP。

    那么,服務請求者如何知道請求消息應該使用什么格式呢?這個問題在下面會得到回答。

     

    服務描述:從 XML 消息傳遞到 Web 服務

    服務提供者是通過服務描述將所有用于調用 Web 服務的規范傳送給服務請求者的。要實現 Web 服務體系結構的松散耦合,并減少服務提供者和服務請求者之間所需的共識的程度和定制編程與集成的程度,服務描述就是關鍵。例如,不管是請求者還是提供者,都不必了解對方的底層平臺、編程語言或分布式對象模型(如果有的話)。服務描述與底層 SOAP 基礎結構相結合,足以封裝服務請求者的應用程序和服務提供者的 Web 服務之間的這個細節。

    基本服務描述

    Web 服務體系結構使用 WSDL 作為基本服務描述。WSDL 已經被提交到 W3C 作為標準。WSDL 是一種 XML 文檔,它將 Web 服務描述為一組端點,這些端點會處理包含面向文檔或面向過程的(RPC)消息的消息。操作和消息都是被抽象描述的,然后它們會被綁定到一個具體的網絡協議和消息格式,用來定義端點。相關的具體端點被合并到抽象的端點或服務中。WSDL 可以擴展為允許端點和其消息的描述,不管使用哪種消息格式或網絡協議進行通訊都可以。然而,目前經過描述的綁定只能用于 SOAP 1.1、HTTP POST 以及多用途互聯網郵件擴展(Multipurpose Internet Mail Extensions,MIME)。

    Web 服務體系結構中對 WSDL 的使用按照常規將基本的服務描述分成了兩部分:服務接口和服務實現。這使每個部分都可以被分開獨立定義,并可以由另一部分重新使用。

    服務接口定義是一種抽象或可重用的服務定義,它可以被多個服務實現定義實例化和引用。我們可以將服務接口定義想象成接口定義語言(Interface Definition Language,IDL)、Java 接口或 Web 服務類型。這使常見的行業標準服務類型可以被多個服務實現者定義和實現。這類似于在編程語言中定義抽象接口然后得到多個具體實現。服務接口可以由行業標準組織定義。

    服務接口包含 WSDL 元素,它們組成了服務描述中的可重用部分,這些元素有:WSDL: binding、WSDL: portType、WSDL: message 和 WSDL: type 元素,如圖 5 中所描述。WSDL: portType 元素中定義了 Web 服務的操作。操作定義了輸入和輸出數據流中可以出現的 XML 消息。您可以將操作想象成編程語言中的方法說明。WSDL: message 元素指定哪些 XML 數據類型組成消息的各個部分。WSDL: message 元素用于定義操作的輸入和輸出參數。WSDL: types 元素中描述消息中復雜數據類型的使用。WSDL: binding 元素描述特定服務接口(WSDL: portType)的協議、數據格式、安全性和其它屬性。

    服務實現定義是一個描述給定服務提供者如何實現特定服務接口的 WSDL 文檔。Web 服務被建模成 WSDL: service 元素。服務元素包含一組(通常是一個)WSDL: port 元素。端口將端點(例如網址位置或 URL)與來自服務接口定義的 WSDL: binding 元素關聯起來。

    為了說明職責的安排,開放應用程序組(Open Applications Group,OAG)為開放應用程序組集成規范(Open Applications Group Integration Specification,OAGIS)購買標準定義了一個服務接口定義。這個服務接口定義會定義 WSDL: type、WSDL: message、WSDL: portType 和 WSDL: binding。

    服務提供者可以選擇開發實現 OAGIS 購買訂單服務接口的 Web 服務。服務提供者會開發一個服務實現定義文檔,描述 WSDL 設備、端口和地址位置元素,這些元素描述提供者的 Web 服務的網址及其它特定于實現的細節。

    服務接口定義和服務實現定義結合在一起,組成了服務完整的 WSDL 定義。這兩個定義包含為服務請求者描述如何調用以及與 Web 服務交互的足夠信息。服務請求者可以要求獲得其它關于服務提供者端口的信息。此信息由服務完整的 Web 服務描述提供。

     

    完整的 Web 服務描述

    完整的 Web 服務描述建立在服務基本的 WSDL 定義之上。完整的 Web 服務描述可以解決這樣的問題:什么企業在托管這個服務?它是何種類型的企業?與服務相關聯的產品有哪些?各種公司和產品類別中與該企業或其 Web 服務相關聯的分類有哪些?有沒有服務的其它方面(如服務質量)會影響到請求者是否選擇調用服務?為了使查找該服務更容易,可以提供哪些關鍵字?

    圖 6 中描述了一個完整的 Web 服務描述。

    圖 6. 完整的 Web 服務描述協議棧

    UDDI 提供了一個保存 Web 服務描述的機制。雖然 UDDI 通常會被認為是一種目錄機制,但是它也定義了一個用 XML 表示服務描述信息的數據結構標準。UDDI 條目中有四種基本數據結構,如圖 7 中所示。

    圖 7. 基本 UDDI 數據結構

    UDDI 條目由 businessEntity 開始。businessEntity 元素對關于企業的信息進行建模,包括基本的企業信息(例如企業名稱和聯系方式信息是什么?)、分類信息(例如這是何種類型的企業?)以及標識信息(即 Dunn and Bradstreet 編號是什么?)。businessEntity 包含一組 businessService 元素,每個元素對應于企業希望發布的每個 Web 服務。每個 businessService 元素都包含和 businessEntity 元素的 Web 服務有關的技術性和描述性信息。businessService 包含一組 bindingTemplate 元素。bindingTemplate 描述訪問信息(例如端點地址),還描述 businessService 如何使用各種不同的技術規范。技術規范在這里的模型是 tModel。tModel 可以為很多不同概念建模,如:一種服務、一個諸如 HTTPS 之類的平臺技術或一個類別。與 businessService 相關聯的那一組 bindingTemplate 元素代表了 businessService 所使用的技術的印記。

    在Web 服務體系結構中,完整的 Web 服務描述包括用于端點描述的一層,這個端點描述使用 UDDI 條目向服務描述添加企業和實現環境。

    端點描述遵循結合 WSDL 使用 UDDI 的約定。端點描述使用 UDDI 提供企業信息和類別的標準表示。這個 UDDI-WSDL 約定規定了如何從和 Web 服務相關聯的 UDDI 條目中得出服務接口定義和服務實現定義的 WSDL 描述。這個約定對于在Web 服務體系結構中使用 UDDI 作為基于 WSDL 的服務的服務注冊中心來說至關重要。

    端點描述向應用到服務的特定實現的服務描述添加了另外的語義。安全屬性可以定義對 Web 服務的訪問進行控制的策略。服務質量屬性指定面向性能的能力,例如服務在一定時間內作出響應的能力,或所支持的可靠消息傳遞的級別。服務開銷屬性描述服務的資源需求。還可以定義支持哪些對話語義。

    服務描述協議棧中的最后一層是協議描述。協議描述反映兩個企業伙伴之間為了完成一個多步企業交互而進行的 Web 服務調用的一個簡單的編排。例如,“協議定義”定義了購買協議中諸如購買者和出售者之類的角色。協議定義規定了每個角色必須達到的要求。例如,出售者必須有接受報價請求(request for quote,RFQ)消息、購買訂單(purchase order,PO)消息和付款消息的 Web 服務。購買者的角色必須有接受報價(RFQ 響應信息)、發票消息和帳戶摘要信息的 Web 服務。這個簡單的 Web 服務到企業角色的編排對于在企業伙伴之間建立多步的、面向服務的交互來說至關重要。在很多不同的企業協議標準下,一個給定的服務請求者或服務提供者也許能夠扮演購買者或出售者的角色。通過顯式地建立企業協議和每個節點在企業協議中扮演各種角色的能力,請求者可以選擇在面對各種提供者企業伙伴時加入哪種企業協議。

    這個領域充滿了創新。對于企業協議定義來說,目前還沒有一個單獨的標準。ebXML 協作-協議概要和協定規范(ebXML Collaboration-Protocol Profile and Agreement Specification)描述了這些概念,但不是根據作為該體系結構的一部分描述的 Web 服務技術而描述的。Web 服務流程描述和 Web 服務端點描述這兩層正處于開發中,它們可以提供這個級別的服務描述。

     

    服務描述的發布和發現

    服務發布

    Web 服務的發布包括服務描述的生成和之后的發布。發布可以使用各種不同機制。

    生成服務描述 
    我們可以生成、手工編碼服務描述,也可以根據已有的服務接口定義組成服務描述。開發者可以手工編碼整個服務描述,包括 UDDI 條目。有些工具可以從編程模型和可執行 Web 服務的部署生成 WSDL,還有可能生成來自元數據構件的部分 UDDI 條目。部分服務描述可能已經存在(例如,Web 服務可以基于一個行業標準服務接口定義),這樣就只須進一步生成一小部分就可以了。

    發布服務描述 
    服務描述可以使用各種不同機制來發布。根據應用程序將使用服務的動態程度,這些不同的機制提供不同的能力。服務描述可以使用多種不同機制發布到多個服務注冊中心。

    最簡單的情況是直接發布。直接發布意味著服務提供者直接將服務發布給服務請求者。這可以通過使用電子郵件附件、FTP 站點甚至光盤分發來實現。直接發布可以在企業伙伴雙方就在 Web 上使用電子商務的條款達成一致后進行,或在請求訪問服務的服務請求者支付了費用之后進行。在這種情況下,服務請求者可以保留服務描述的一份本地副本。

    稍微更動態一點的發布使用 DISCO 或 ADS。DISCO 和 ADS 兩者都定義了一個從給定 URL 獲取 Web 服務描述的簡單的 HTTP GET 機制。增強的服務描述資源庫會提供服務描述的一個本地高速緩存,不過還提供了附加的搜索能力。對于在企業內部跨越主機的服務描述資源庫來說,服務提供者會向專用的 UDDI 節點發布。我們可以根據發布到節點的 Web 服務的域的范圍,使用幾種專用的 UDDI 節點。

    內部企業應用程序 UDDI 節點(Internal Enterprise Application UDDI node)節點:公司內部為了進行內部企業應用程序集成而使用的 Web 服務應該被發布到這一類 UDDI 節點。此類 UDDI 節點的范圍可以是部門的或公司的單獨的應用程序。這些 UDDI 位于防火墻之后,允許服務發布者對他們的服務注冊中心和它的訪問權、可用性以及發布要求有更多的控制。 
    門戶網站 UDDI 節點(Portal UDDI node)節點:由公司發布以供外部伙伴查找和使用的 Web 服務可以使用門戶網站 UDDI 節點。門戶網站節點運行在服務提供者的防火墻之外或之間。這種專用 UDDI 節點只包含公司希望向來自外部伙伴的請求者提供的那些服務描述。這允許公司保留對他們服務描述的控制、UDDI 節點的訪問以及 UDDI 節點的服務質量。此外,通過使用門戶網站中固有的基于角色的可見性,企業將服務描述的可見性局限在允許看到它們存在的伙伴中。 
    伙伴目錄 UDDI 節點(Partner Catalog UDDI node)節點:由特定公司使用的 Web 服務可以被發布到伙伴目錄 UDDI 節點。伙伴目錄 UDDI 節點位于防火墻之后。此類專用 UDDI 節點只包含來自合法企業伙伴的經過允許的、測試過的、有效的 Web 服務。此類 Web 服務的業務環境和元數據可以被定位到特定的請求者。 
    電子市場 UDDI 節點(E-Marketplace UDDI node)節點:對于服務提供者打算用來與其它 Web 服務競爭請求者的業務的 Web 服務來說,服務描述應該被發布到電子市場 UDDI 節點或 UDDI 運營商節點。電子市場 UDDI 節點由一個行業標準組織或社團托管,包含特定行業中的企業的服務描述。我們可以要求這些服務支持特定的標準、可搜索元數據、接口或數據類型。電子市場 UDDI 節點一般會過濾掉某些非法的條目,并提供有保證的服務質量。 
    UDDI 運營商節點:如果您希望 Web 服務可以被潛在的新的企業伙伴或服務用戶發現,還可以將其發布到 UDDI 運營商節點。IBM、Microsoft 和 Ariba 都支持、復制和托管 UDDI 運營商節點。在發布 UDDI 運營商節點的時侯,如果要讓潛在的服務請求者發現服務的話,完整的業務環境和經過深思熟慮的分類法是很必要的。

    圖 8. 服務發現連續體

    圖 8 展示了從發布和發現中最靜態、最簡單的技術到最動態、更復雜的技術的連續體。Web 服務的用戶或實現者不必嚴格遵循這個發展順序。

    服務發現

    Web 服務的發現包括獲取服務描述和使用描述。獲取過程可以使用各種不同機制。

    獲取服務描述

    和發布 Web 服務描述一樣,根據服務描述如何被發布以及 Web 服務應用程序可能達到的動態程度,獲取 Web 服務描述也會有所不同。服務請求者將在應用程序生命周期的兩個不同階段,即設計時和運行時查找 Web 服務。在設計時,服務請求者按照他們支持的接口類型搜索 Web 服務描述。在運行時,服務請求者根據他們通訊的方式或公告的服務質量搜索 Web 服務。

    使用直接發布方法時,服務請求者在設計時對服務描述進行高速緩存,以在運行時使用它。服務描述可以被靜態地用程序邏輯表示,并存儲在文件或簡單的本地服務描述資源庫中。

    服務請求者可以在設計時或運行時在服務描述資源庫(簡單的服務注冊中心或 UDDI 節點)中檢索一條服務描述。查找機制需要支持一種查詢機制,它提供按接口類型(基于 WSDL 模板)、綁定信息(即協議)、屬性(如 QoS 參數)、所需的中介類型、服務分類法、企業信息等等的查找。

    不同類型的 UDDI 節點會顯示可以選擇的運行時綁定 Web 服務的數目、多選一的策略,或者調用服務之前必須由請求者作出預選的量。

    內部企業應用程序 UDDI 節點和伙伴目錄 UDDI 節點將不需要預選來建立對服務的信任。服務選擇可以建立在綁定支持、歷史性能、服務質量分類、相似性或負載平衡的基礎之上。

    電子市場 UDDI 節點將有更多的運行時服務可以選擇。必須執行某種預選以保證 Web 服務提供者是有價值的伙伴。我們可以根據價格承諾、開銷、經過允許的伙伴列表的出席情況,同樣還有綁定支持、歷史性能、服務質量分類和相似性來選擇服務。

    如果服務請求者從 UDDI 運營商節點查詢 Web 服務提供者,他們在預選可能的服務提供者時就必須盡可能謹慎和認真。應該有一個有效和準確的機制就位,過濾掉無用的服務描述和沒有價值的服務提供者。

    使用服務描述 
    在獲取了服務描述之后,服務請求者需要處理它以調用服務。服務請求者使用服務描述生成對 Web 服務的 SOAP 請求或特定于編程語言的代理。該生成可以在設計時或運行時進行,從而對 Web 服務的調用進行格式化。我們在設計時和運行時可以使用各種工具從 WSDL 文檔生成編程語言綁定。這些綁定表示應用程序的 API,并封裝了來自應用程序的 XML 消息傳遞的細節。

    在下一部分,我們將描述基本 Web 服務體系結構的擴展,電子商務需要這些擴展才能使用 Web 服務。

     

    在下一部分,我們將描述基本 Web 服務體系結構的擴展,電子商務需要這些擴展才能使用 Web 服務。

     

    14.3.2 真正的電子商務的 Web 服務

    雖然對于可互操作的 XML 消息傳遞來說 SOAP 和 HTTP 就足夠了,而且 WSDL 也足可以傳達服務請求者和服務提供者之間需要什么樣的消息,但是要覆蓋電子商務的全部需求還需要更多的技術。為了完全支持電子商務,安全性、可靠的消息傳遞、服務質量、Web 服務協議棧的每一層的管理都需要擴展。

     

    安全性

    真的需要 Web 服務安全層嗎?對于基于消息的體系結構,業界已經有一套現成的而且廣泛接受的傳輸層安全機制,比如,安全套接字層(Secure Sockets Layer,SSL)和網際協議安全(Internet Protocol Security,IPSec),為什么還要再加別的呢?為了回答這個問題,我們不僅要研究要求,還將探討一些只依靠現有的幾類傳輸層安全機制并不能在 Web 服務模型內提供足夠的安全性的情況。

    通常,Web 服務安全層必須提供以下四個基本的安全性要求:

    機密性(Confidentiality)是指信息對沒有經過授權的個人、實體或進程的不可用性或不公開性,并保證消息內容不對沒有經過授權的個人公開。 
    授權(Authorization)是指權限的授予,包括根據訪問權限授予訪問權和保證發送方被授權發送消息。 
    數據完整性(Data integrity)是指數據沒有以未經授權的方式或被未經授權的用戶不可察覺的改變或者破壞的性質,從而確保消息在傳送的過程中不會被偶然或故意修改。 
    原始性證明(Proof of origin)是對消息或數據的發送者進行標識的證據。斷言消息由正確標識的發送者傳送,并且不會重新發送以前傳送過的消息。這一要求隱含了數據完整性的要求。 
    由于需要在基于 XML 消息和工作流的動態 Web 服務世界中管理不同風格的資源訪問,所以必須重新評估策略、信任和風險評估這三者相互之間的關系。現有的基于個人身份的訪問控制模型正在發展成為基于角色的信任域關系,在該種關系中,可信任的權威機構將執行某項任務的權限授予個人,其行為受該權限限制。Web 服務體系結構定義了需要信息的代理(服務請求者)、提供信息的代理(服務提供者),有時還有提供關于信息的信息的代理(服務中介者、元信息提供者或服務注冊中心)。服務中介者經常會收到大量信息請求,這樣就需要它能夠決定誰想要哪些信息以及請求者是不是已經被授予訪問權。基礎設施和關系變化迅速,因此有關的策略需要能靈活的允許或拒絕訪問。

    此外,盡管 XML 發誓要為這樣的服務提供通用接口,但 XML 不會提供實現這一夢想所需要的整個基礎設施。而且,XML 可能不適合構建整個 Web 服務安全層。目標是要確定在哪些場合用 XML 格式提供信息以顧及通用數據交換較為重要,以及在哪些場合利用目前已存在于平臺之上的現有安全性機制較為重要。

    SOAP 信封是用 XML 定義的,從而使您可以向消息添加種類眾多的元信息,比如事務 ID、消息路由信息和消息安全性。SOAP 信封由兩個部分組成:頭和主體。頭是把功能添加到 SOAP 消息中的通用機制。SOAP 頭元素下一級的所有子元素都叫做頭條目。主體是為最終的消息接收方想要的應用數據(如 RPC)準備的容器。因此,可以把 SOAP 看作是在傳輸層(例如 HTTP)和應用層(例如,業務數據)之間引入的另外一層,在此可以方便的傳送消息元信息。SOAP 頭提供可擴展機制以擴展 SOAP 消息使其可以適用于多種用途。雖然 SOAP 頭是向消息添加安全性功能最合理的地方,但是 SOAP 規范本身并沒有指定這樣的頭元素。

    讓我們仔細的分析一下在 Web 服務模型中現有的各種各樣的傳輸層安全機制為什么不夠,又為什么會需要 Web 服務安全層,以及這個安全層最初是怎樣的。

    端對端的消息傳遞。安全傳輸協議,如 SSL 和 IPSec,可以在傳輸過程中提供消息完整性和機密性,但只有在點對點的情況下,它們才會這樣做。但是,因為 SOAP 消息是由中介體接收并處理的,所以即便兩兩之間的通信鏈路(communication link)是可信任的,只要在所有的中介體間沒有信任關聯(trust association),那么安全的端對端通信就是不可能的。如果有一條通信鏈路不安全,那么端對端安全性也會被削弱。就 Web 服務拓撲來看,安全的傳輸對于 SOAP 消息的端對端安全性是不夠的。

    中間件的獨立性。最終,唯一能提供端對端安全性的方式就在于應用層或中間件層。如果消息在通信方之間的某點是純文本,那么就有可能在這點受到攻擊。但是,既要在新的或現有的應用中集成加密功能,又不能引入額外的安全性弱點或增加風險,這是一項不容易又不受歡迎的任務。因此,在大多數情況下,人們希望安全性功能盡可能靠近應用,但不在應用本身中構建。

    傳輸的獨立性。SOAP 中介體的原意是用來把信息轉發到不同的網絡上去,通常使用的傳輸協議也會有所不同。雖然所有的通信鏈路都是安全的,中介體也是值得信賴的,但是,安全信息(如消息發送者的身份驗證)需要被轉移到消息路徑上的下一個傳輸協議安全性域,這個過程冗長而且復雜,還可能會導致完整性方面的缺陷。

    異步多階消息傳遞。傳輸層安全性保證數據在通信鏈路上傳輸時的安全。它與存儲在任何中介體上的數據都無關。在一次傳輸被接收并解密后,傳輸層安全性對保護數據免受沒有經過授權的訪問和可能的改變就不是很有幫助了。在先存儲消息然后轉發的情況下(持久的消息隊列),消息層保護是有必要的。

    因為我們已經看到安全的傳輸機制不足以滿足 Web 服務開發方法和使用場景的要求,所以我們的任務就是要創建一個概念性 Web 安全層,包括下列幾個組件:

    對于網絡安全性: 
    支持如 SSL 和 HTTPS 等提供機密性和完整性的安全傳輸機制。 
    對于 XML 消息: 
    如果通信沒有中間節點,那么發送方可以依靠 SSL 或 HTTPS 來保證用戶標識和密碼的機密性。 
    W3C 正在標準化 XML 數字簽名工作的支持。它定義了生成消息摘要與利用發送方的私鑰來簽發消息的標準 SOAP 頭和算法。因此,接收方就可以證明消息發送方的身份。 
    對網絡內部的、可信任的第三方驗證服務(例如 Kerberos)的支持。 
    概念性 XML 消息傳遞模型還必須支持端對端保護消息及其子元素。為了全面的支持,過程和流能力需要被擴展到包括消息交換的安全性特征。應該有一種方式可以定義多段消息和用預期接收方的公鑰來保護消息段。需要探討的一些論題有:

    端點負責實現驗證及授權。應該支持在企業之間交換信息的合同的任何描述中都要定義哪些雇員可以使用哪些服務。中介體負責審計和服務原始性證明。中介體還可能需要執行驗證、授權和數字簽名驗證以及有效性檢查。 
    在服務端點的服務描述層中需要定義支持上文論述的安全性問題的面向安全性元數據。這些安全性描述將根據主體或角色定義 Web 服務層訪問控制。服務描述將會描述是否支持數字簽名、加密、驗證和授權以及如何支持它們。 
    請求者將使用服務描述的安全性元素來查找服務端點,該端點應符合政策要求及其安全性方法。 
    標準組正在調查如下主題和技術。隨著這些標準固定下來,它們將會被并入 Web 服務安全性體系結構。

    W3C 有一個 XML 加密工作組,幫助提供數據元素的機密性,這樣驗證交換成為可能。 
    W3C 已發布了一個 XML 密鑰管理服務(XML Key Management Services,XKMS)的備忘錄,來幫助分發及管理在端點之間進行安全的通信所需的密鑰。 
    OASIS 已經成立了一個技術委員會來定義授權和驗證斷言(Authorization and Authentication assertions,SAML)。這將幫助端點接受和決斷訪問控制權。 
    OASIS 已經成立了一個技術委員會來標準化訪問控制權的表達(XACML)。這將幫助端點能夠以一致的方式解析 SAML 斷言。 
    隨著我們不斷的研究 Web 服務模型中遇到的所有威脅和對策,Web 服務安全性體系結構也在不斷發展著。

     

    服務質量(QoS)和可靠的消息傳遞

    服務質量垂直塔提供與 Web 服務概念棧每一層有關的信息的規范。對于網絡層,這將會暗示能使用各種級別的服務質量的網絡。

    由于需要通過網絡進行可靠的消息傳遞,所以得根據在這一領域內發送高質量服務的能力來選擇網絡技術。可靠的消息傳遞指基礎設施把消息一次發送(只發送一次)到預定目標或提供確定的事件(如果發送沒能完成,也許會重新發送到源)的能力。結合網絡層與 XML 消息傳遞將需要支持四個等級的消息傳遞服務質量:

    1. 最佳努力:服務請求者發送消息,服務請求者和基礎設施不嘗試重發。

    2. 至少一次:服務請求者提出請求,并一直重試直到它接收到確認為止。服務提供者重復消息處理不是問題,例如簡單的查詢處理。實現這可能意味著每個消息包含唯一的標識。服務請求者以自己確定的時間間隔重發沒有得到確認的消息。服務提供者發出確認消息,為 RPC 響應消息,如果不能處理的話,就發送不能處理的消息異常。

    3. 至多一次:這建立在最少一次情況的基礎之上。服務請求者試著請求直到它得到回應。象現有的全局唯一標識符(universal unique identifier,UUID)這樣的機制允許服務提供者抑制重復多次的請求,以確保請求不會被多次執行。例如,請求根據庫存目錄中的一個號碼拿一件東西。

    4. 剛好一次:服務請求者提出請求,請求已經執行的回應使其得到保證。剛好一次交換模式排除了重傳請求的需要并且適應失效的情況。

    可靠的消息傳遞通常是通過標準設計模式傳送的,在該模式中,一件基礎設施,有時叫做端點管理器,將會被用來在通信的每一端協調消息發送。在這種模式中,發送方通過同步請求把消息發給端點管理器。一旦發送到,發送方就可以得到保證,一定會把消息發送出去或引發確定的事件(例如超時)。端點管理器與其它資源管理器參與本地事務,在一次事務中不僅可以由端點管理器對消息進行排隊,還有可能在數據庫中記錄業務過程步驟。應用程序應該指派端點管理器來負責發送消息或者檢測發送失敗的原因,它在網絡傳輸層或 XML 消息傳遞層都可以起作用。

    可靠的一次性消息發送的技術和目的都不會引起爭議。但是,圍繞如何在 SOAP 和 XML 的上下文中支持這項技術已經提出了重要的質疑。關鍵問題是:應不應該在 XML 消息層上定義必要的協議和消息格式,從而允許可靠的消息發送成為兩端應用程序的責任,或者能不能在較低的層(如傳輸層上)定義協議和消息格式?

    在沒有支持可靠的消息傳遞的傳輸方式的情況下(即互聯網),XML 消息傳遞層將需要在不可靠的基礎設施之上支持這些服務質量。端點管理器將需要修改消息,而不是修改消息的傳輸信封,這樣才能成功扮演其角色。應用程序和業務過程的定義將必須考慮所有可能的結果,如拒收消息或在可接受的時間長度內發送不出去。但是,這些定義還需要考慮在發送過程中發生的中間狀態。向業務過程公開這些狀態可能會大大增加其定義的復雜程度,但對于定義過程的業務分析師而言并無太大意義。在一些情況下,使用 XML 消息傳遞來發送可靠的消息傳遞格式可能會導致使用現有的這些傳輸毫無效率。最好能開發一種在互聯網上使用的可靠的 HTTP 標準。

    在有支持可靠的消息傳遞的傳輸方式的情況下(即在企業內部),它可以用于發送可靠的消息,而不是 XML 消息傳遞層(可能缺省為空實現)。端點管理器將會只修改傳輸信封,而不會修改 XML 消息。使用可靠的傳輸使應用程序和業務過程定義不需要知道或處理消息發送的中間狀態。

    需要在將來進行的幾點補充:

    互聯網的 HTTP 需要加以改進才能提供可以在企業間使用的簡單可靠的消息傳遞。這會帶來額外的好處:不止 SOAP,許多種消息類型都可以采用可靠的消息傳遞。需要 XML 消息傳遞層處理可靠的消息傳遞的情況就會隨之減少,促進不依賴于網絡選擇的應用程序開發。 
    HTTP 上的 XML 消息傳遞層也需要處理發布和訂閱、消息排序、發送時間限制、優先級和多點傳送等等問題。 
    服務提供者對可靠的消息傳遞的質量和實現的支持情況將會在服務描述的綁定信息中定義。

    服務實現層(例如,通過事務的或安全的 SOAP 綁定)的服務描述以及接口層(例如,從請求者開始等待來自提供者的響應之后最長經過多久)的其它服務描述中都會關系到服務質量(Quality of Service)信息。人們期待著開發出 WSDL 擴展或新的服務描述層來允許指定其它服務質量和功能的規范。

    Web 服務層上的服務質量可以在服務合成和服務流中使用。在為流選擇服務或提示流管理器該開始恢復或其它的流時,預期的執行時間、超時值、歷史平均執行時間值都可以作為輸入。服務描述棧的端點描述層和工作流描述層必須提供這一信息。

    Web 服務的服務質量問題和解決方案仍然很緊迫。

     

    系統和應用程序管理(Management)

    隨著 Web 服務成為商業運作的重要因素,就需要對其進行管理。在這種情況下,所謂管理是指專為應用程序定制的或從廠商那里買來的管理應用程序可以發現 Web 服務的基礎設施、Web 服務、服務注冊中心和 Web 服務應用程序存在性、可用性以及健康度。最令人滿意的結果是管理系統還應當能夠控制和配置基礎設施及組件。

    管理概念性 Web 服務棧各層的 Web 服務和 Web 服務模型組件必定是有可能的。對管理的需求可以分成兩個集中的領域。第一個領域是用于實現 Web 服務的基礎設施的可管理性。主要的考慮應當是確保可用性和提供服務描述、消息傳遞和網絡的關鍵元素的性能。Web 服務基礎設施提供者應當提供這一層上的系統管理。

    企業對其自己的基礎設施及管理擁有完全的自主權。但是,當企業在對等基礎上相互作用時,就應當提供對網絡層、XML 消息傳遞層、服務注冊中心和 Web 服務實現的基本報告和恢復辦法。此外,企業向其合伙人提供的管理接口應當是在服務層上操作的,而不是在相對低級的基礎設施層上。合伙人應該能夠訪問到報告操作和請求處理的狀態和健康度的接口,但不一定要理解企業如何管理其請求的細節。

    對于網絡層,現有的網絡管理產品幾乎支持目前所有的網絡基礎設施。這些產品應當用于管理企業內部的 Web 服務的網絡基礎設施。當企業相互作用時,就應該向其合伙人提供有關 Web 服務基礎設施可用性的基本報告。影響 Web 服務基礎設施可用性的網絡可用性應作為因素之一寫入報告。

    在 XML 消息傳遞層,協議應該在企業內部由現有的基礎設施管理工具來管理。在企業相互作用的情況下,每個站點都有必要提供協議的基本報告和恢復辦法。例如,如果站點 A 支持會話,就該向站點 B 提供可用于查詢活躍的 IBM Software Group Architecture Overview Web Services Conceptual Architecture 28 會話以及強行回滾的接口。協議層需要正常的頻道與協議和類似對等的控制接口。

    管理的第二個方面是 Web 服務本身的可管理性。一些主要的考慮是性能、可用性、事件和使用量度,因為它們將為服務提供者市場收取所提供的服務使用費提供必要信息。

    服務描述可以用于宣傳可管理性特征和管理需求。這方面的約定正在開發之中。

    服務注冊中心的任何實現,不管是用于私人消費還是公共消費,都要求基礎設施是可用的、發送承諾的服務質量并能夠報告使用情況。這些系統管理元素對于成功采用 UDDI 是十分重要的。

    對于 Web 服務應用程序組件來說,支持管理環境可能會大大增加應用程序的復雜性。由于 Web 服務必須易于開發,所以必須盡可能向開發者隱藏這樣的復雜性。Web 服務的管理方式要使基礎設施能自動提供量度、審計日志、啟動和停止處理過程、事件通知和作為 Web 服務運行時的一部分(也就是說,起碼是 SOAP 服務器)的其它管理功能。因為基礎設施通過觀察它所托管的組件的行為不可能收集到所有的信息,所以 Web 服務實現也許會需要向托管它的服務器提供基本的健康度和監督信息。

    Web 服務基礎設施應該為服務提供一種簡單的方式以參與管理和利用管理基礎設施。可管理的服務的 WSDL 文檔的定義應當是 Web 服務能實現提供通過管理系統訪問 Web 服務的管理信息的功能。這一接口可能包括的能力是獲得配置和量度數據、更新配置及接收來自可管理的 Web 服務的事件。

    Web 服務體系結構的平臺獨立性使它不適合套用任何一條 Web 服務管理標準。因此,需要有一種基于 Web 服務而且允許 Web 服務與管理系統通信的方法。為了達到這一目的,還應當定義由 WSDL 文檔描述的、可接收來自可管理 Web 服務的事件以及量度更新的管理服務,并使其可用。管理服務的實現技術與 Web 服務無關。但是,對于基于 Java 技術的環境,Java 管理擴展(Java Management Extension,JMX)應該是合乎邏輯的而且廠商不可知的選擇。通過使用 JMX 這樣的開放標準,對現有的系統管理提供者來說,要把其目前所提供的產品擴展為包括 Web 服務關鍵元素的管理應該是很容易的。Web 服務的管理體系結構仍在向前發展。

     

    14.4 Web Services 項目實戰

    14.4.1 Web Services實現

     

    本書是重點講解EJB 3.0的。在理解了Web Services原理之后, 接下來我們講解如何使用J2EE和EJB3.0來實現Web Services

     

    Web 服務遵循 Java 2 平臺,企業版(Java 2 Platform,Enterprise Edition,J2EE)、通用對象請求代理體系結構(Common Object Request Broker Architecture,CORBA)以及其它針對與耦合較緊的分布式或非分布式應用程序集成的標準。Web 服務是部署并提供通過 Web 訪問業務功能的技術;J2EE、CORBA 和其它標準是實現 Web 服務的技術。

     

    J2EE 1.4為使用常規Java類或企業級Java Beans來創建和部署web services提供了一個全面的平臺。以下表格給出了J2EE 1.4中包括的web service APIs的細節。 

    定義在Java Community Process的JSR 101之下的JAX-RPC,提供了創建和訪問web services的Java API,因此它是使用J2EE平臺創建和部署web services的“心臟和靈魂”。通過向應用程序開發者隱藏XML類型和Java類型映射的復雜性,以及處理XML和SOAP消息的底層細節,它提供了一個簡單的,健壯的創建web services應用的平臺。為了引入一個方法調用范式,它提供了兩種編程模式:服務器端模式,使用Java類或無狀態EJB開發web service 端點,和客戶端模式,創建作為本地對象訪問web services的Java客戶端。JAX-RPC 1.1要求使用SOAP 1.1,并且實現與使用其他技術創建的web services之間的互操作性,比如微軟的.NET。實現了J2EE1.4規范的應用服務器,比如OC4J 10.1.3和SUN的Java System Application Sever,提供了對于JAX-RPC的支持。

    JAX-RPC的叫法有點用詞不當,因為它既支持RPC類型的web services,也支持文檔類型的web services。

    Web Services部署模型

    在J2EE 1.4之前,所有J2EE商家都使用他們私有的部署模型支持web services。J2EE 1.4為Java Web Services定義了部署模型。它為J2EE平臺上的web services制定了開發,部署以及服務發布和使用的標準。

    有了J2EE 1.4對web services的支持,讓我們學習使用J2EE平臺來建造web service的方法。

    使用J2EE創建一個Web Service

    把web service創建成一個輕便的和可互操作的分布式組件不是一項瑣碎的任務。如之前討論的,你既可以把常規Java類,也可以把無狀態EJB部署成web services。常規Java類被打包在一個web模塊中,而EJB web services被打包在標準的ejb-jar模塊中。

    在這兩種部署選擇中,你會使用哪一個呢?

    Java 類對無狀態EJB:永無止境的爭論

    你會選擇常規Java類還是EJB作為你創建web service的技術可能是一個長期的爭論。Java類比EJB更容易開發,它是純的Java對象,并且它不具有EJB帶來的“額外輜重”。但是,EJB提供了幾個很好的特點,比如被聲明的事務和安全性,因此它使開發者將精力集中于建立商業邏輯,而不需要擔心基礎服務架構。EJB 3.0大大簡化了設計模型,在它的規范中,EJB看起來就像常規Java類。

    使用J2EE 5.0簡化SOA的開發

      使用J2EE創建面向服務的應用程序確實很困難,因此通過使用由JSR 181定義的Web Services 元數據注解,J2EE 5.0將使開發更簡單。EJB 3.0和Web Services元數據具有相似的目標,就是向開發者提供親和力。

      為了在J2EE 1.4中開發一個簡單的Java web service,你需要幾個web service定義文件:WSDL,映射文件和幾個冗長的標準以及私有的web services部署描述符。Web Services元數據規范使用一種類似于EJB 3.0的缺省配置方法來使開發更簡便。Web Services元數據注解處理器(或web services 裝配工具)會為你生成這些文件,因此你只需要關心類的實現。

      當你使用Web Services元數據開發時,這是一個看起來如此簡單的Java web service:

    package com.ascenttech.ejb30.ws.demo; 
    import javax.jws.WebMethod; 
    import javax.jws.WebService; 
    @WebService(name = "HelloWorldService", 
    targetNamespace = "http://hello/targetNamespace" ) 
    public class HelloWorldService { 
    @WebMethod public String sayhello(String name ) { 
    return "Hello” +name+ “ from jws";

    }

      正如我之前提到的,EJB 3.0使用常規Java類簡化了EJB的開發。通過利用EJB 3.0和Web Services元數據,開發基于EJB的web services將會變得越來越簡單。當使用EJB 3.0和web services元數據時,這是一個看起來如此簡單的HelloWorld EJB web service。你不必擔心創建WSDL,部署描述符等等,應用服務器會在部署過程中生成這些定義文件。

    package com.ascenttech.ejb30.ws;
    import javax.ejb.Remote;
    import javax.jws.WebService;
    @WebService 
    public interface HelloServiceInf extends java.rmi.Remote{
    @WebMethod java.lang.String sayHello(java.lang.String name) 
    throws java.rmi.RemoteException;
    }

      如下是EJB 3.0中 HelloWorld EJB的實現類:

    package com.ascenttech.ejb30.ws;
    import java.rmi.RemoteException;
    import javax.ejb.Stateless;
    @Stateless(name="HelloServiceEJB")
    public class HelloServiceBean implements HelloServiceInf {
    public String sayHello(String name) {
    return("Hello "+name +" from first EJB3.0 Web Service");
    }
    }

      以上例子清楚的表明了通過使用web services元數據和EJB 3.0,服務開發正在變得越來越簡單。現在,你可以在實現了J2EE規范的應用服務中,比如JBoss Application Server等,開始創建和部署你的web services了。

    14.4.2 Web Services 項目實戰

    14.4.2.1 Web Services的實現
    本節使用EJB 3.0 實現一個web Services登錄的例子。下面我們創建一個工程叫:EmployeeManager。加入用到的EJB 3.0的jar包。

    我們先創建服務器端的實體Bean,這和創建普通的Ejb3.0實體bean是一樣的:

    package com.ascent.ejb.po;

    import java.io.Serializable;

     

    import javax.ejb.Remote;

    import javax.ejb.Stateless;

    import javax.persistence.Column;

    import javax.persistence.Entity;

    import javax.persistence.GeneratedValue;

    import javax.persistence.Id;

    import javax.persistence.Table;

    @SuppressWarnings("serial")

    @Entity

    @Table(name = "usr")

     

    public class User implements Serializable

    {

    private Integer id;

    private String name;

    private String password;

    private String description;

    @Id

    @GeneratedValue

    public Integer getId()

    {

    return id;

    }

    public void setId(Integer id)

    {

    this.id = id;

    }

    @Column(name = "name", nullable = false)

    public String getName()

    {

    return name;

    }

    public void setName(String name)

    {

    this.name = name;

    }

    @Column(name = "password", nullable = false)

    public String getPassword()

    {

    return password;

    }

    public void setPassword(String password)

    {

    this.password = password;

    }

    @Column(name = "description", nullable = true, length = 100)

    public String getDescription()

    {

    return description;

    }

    public void setDescription(String description)

    {

    this.description = description;

    }

    }

    接著創建遠程接口:

    package com.ascent.webservice.bean;

     

    import java.rmi.Remote;

    import javax.jws.WebMethod;

    import javax.jws.WebService;

    import javax.jws.soap.SOAPBinding;

    import javax.jws.soap.SOAPBinding.Style;

    @WebService

    @SOAPBinding(style=Style.RPC)

    public interface LoginDao extends Remote {

    @WebMethod

    public boolean isLogin(String name, String password);

    }

    在LoginDao類中要注意的是Remote接口是要實現的,@SOAPBinding(style=Style.RPC),Soap的綁定方式也是需要的,不然在客戶端是找不到LoginDao 。

    下面創建會話bean:

    package com.ascent.webservice.bean;

    import java.util.List;

    import javax.ejb.Stateful;

    import javax.ejb.Stateless;

    import javax.jws.WebService;

    import javax.persistence.EntityManager;

    import javax.persistence.PersistenceContext;

    import javax.persistence.Query;

    @Stateless

    @WebService(endpointInterface = "com.ascent.webservice.bean.LoginDao")

    public class LoginDaoBean

    {

    @PersistenceContext

    protected EntityManager em;// the manager of entity

    public boolean isLogin(String name, String password)

    {

    // define query sentence

    StringBuffer hql = new StringBuffer();

    hql.append("from User u where u.name='" + name + "'");

    hql.append(" and u.password='" + password + "'");

    // create the query

    Query query = em.createQuery(hql.toString());

    List queryList = query.getResultList();

    // if the result is null

    if (queryList.size() == 0)

    {

    return false;

    }

    // if the user's length greater 1

    if (queryList.size() > 1)

    {

    return false;

    }

    // return single user

    return true;

    }

    }

    至此服務器端的類是建好了,這里又兩個問題,需要說明一下

    A. 兩個類的方法中都沒有拋出異常,可不可以拋出呢? 可以。但到實現SOA的時候會有一些問題,就是異常在經axis 通過WSDL2Java 生成后,在程序運行時有可能會出現找不到的情形。所以本文為了讓大家在仿照這個例子時都能成功,所以也就拋棄了異常的拋出。

    B. 兩個類的方法的返回值是基本類型,而不是自定義類型,為什么會這樣呢,自定義類型可以不可以呢,可以。

    現在我們的webservice的服務器端就已經創建好了,我們把服務器端的三個類和persistence.xml文件一起打包部署到jboss服務器里。包名是:empdEjb。

    下面我們來創建客戶端:

    package com.ascent.webservice.client;

    import java.net.URL;

    import javax.xml.namespace.QName;

    import javax.xml.rpc.Service;

    import javax.xml.rpc.ServiceFactory;

    import com.ascent.webservice.bean.LoginDao;

    public class LoginClient

    {

    public static void main(String[] args) throws Exception

    {

    String userName ="lxl";

    String password = "lxl";

    URL url = new URL("http://localhost:8080/empdEjb/LoginDaoBean?wsdl");

    QName qname =

    new QName("http://bean.webservice.ascent.com/jaws","LoginDaoService");

    ServiceFactory factory = ServiceFactory.newInstance();

    Service service = factory.createService(url, qname);

    LoginDao loginDao = (LoginDao) service.getPort(LoginDao.class);

    boolean isExists = loginDao.isLogin(userName, password);

    if(isExists)

    {

    System.out.println("hello " + userName);

    }

    else

    {

    System.out.println("sorry " + userName + ", you are not user in the system!");

    }

    }

    }

    把服務器端發布出去以后,服務器端會自動發布一個webService,并且生成并發布一個WSDL文件,通過訪問http://localhost:8080/empdEjb/LoginDaoBean?wsdl這個網址是可以找到的。http://bean.webservice.ascent.com/jaws 和 LoginDaoService 字符串,在客戶端程序當中出現了上面兩個字符串,它們在你生成的wsdl 文件中有詳細的描述。在地址欄里輸入http://localhost:8080/empdEjb/LoginDaoBean?wsdl,就可以看到wsdl文件了。這里你所需要做的是按照你自己的情況編寫你自己的客戶端程序。啟動服務器后,在機子上運行客戶端程序就可以了。當然你也可以去編寫自己的jsp客戶端去調用它。wsdl文件的內容如下:

    <definitions name="LoginDaoService"

    targetNamespace="http://bean.webservice.ascent.com/jaws">

    <types/>

    <message name="LoginDao_isLogin">

    <part name="String_1" type="xsd:string"/>

    <part name="String_2" type="xsd:string"/>

    </message>

    -

    <message name="LoginDao_isLoginResponse">

    <part name="result" type="xsd:boolean"/>

    </message>

    -

    <portType name="LoginDao">

    -

    <operation name="isLogin" parameterOrder="String_1 String_2">

    <input message="tns:LoginDao_isLogin"/>

    <output message="tns:LoginDao_isLoginResponse"/>

    </operation>

    </portType>

    -

    <binding name="LoginDaoBinding" type="tns:LoginDao">

    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

    -

    <operation name="isLogin">

    <soap:operation soapAction=""/>

    -

    <input>

    <soap:body namespace="http://bean.webservice.ascent.com/jaws" use="literal"/>

    </input>

    -

    <output>

    <soap:body namespace="http://bean.webservice.ascent.com/jaws" use="literal"/>

    </output>

    </operation>

    </binding>

    -

    <service name="LoginDaoService">

    -

    <port binding="tns:LoginDaoBinding" name="LoginDaoPort">

    <soap:address location="http://lixinli:8080/empdEjb/LoginDaoBean"/>

    </port>

    </service>

    </definitions>

    這樣一個WebService+Ejb 3.0的例子就實現了。

    14.4.2.2 SOA的實現
    本例還是基于前兩個例子的基礎上的,要保證上面的例子是能正常運行的。

    1.WSDL2Java
    從名字上可以看出,是把wsdl 轉化為java的.在上面的例子中我們在服務器端生成了一個wsdl文件,現在要做的就是你把那個wsdl文件給別人或者別的公司,讓他們根據wsdl中所描述的你所提供的服務,去開發一個應用,來訪問你所提供的接口。拿到這個WSDL文件后要做什么呢,to java。我們來看看怎么to java。

    這里,在原來的EmployeeManager工程下面建一個wsdl文件夾,將它放在下面,然后所要做的準備工作是,把包括axis在內的幾個jar包找到,設置在你的classpath里面。然后在命令行下運行WSDL2Java。

    哪幾個jar包?

    C:\axis-1_4\lib\axis.jar;C:\axis-1_4\lib\axis-ant.jar;C:\axis-1_4\lib\commons-discovery-0.2.jar;C:\axis-1_4\lib\commons-logging-1.0.4.jar;C:\axis-1_4\lib\jaxrpc.jar;C:\axis-1_4\lib\log4j-1.2.8.jar;C:\axis-1_4\lib\saaj.jar;C:\axis-1_4\lib\wsdl4j-1.5.1.jar;E:\jboss-4.0.5\server\default\lib\activation.jar;E:\jboss-4.0.5\server\default\lib\mail.jar

    這是我classpath 里面所設置的幾個jar包,后面兩個可以不需要,第二個也可以不需要,后倆個只是保證運行的時候沒有警告。

    1. 將LoginDaoBean.wsdl 放在wsdl文件夾下面。E:\workspace\EmployeeManager\wsdl

    2. 在命令行下進入E:\workspace\EmployeeManager\wsdl

    3. 執行 java org.apache.axis.wsdl.WSDL2Java LoginDaoBean.wsdl

    這個時候,你會發現在wsdl文件夾下面生成了一個目錄,它里面包含了幾個java類。

    LoginDao.java,LoginDaoBindingStub.java,LoginDaoService.java,LoginDaoServiceLocator.java

    2 SOA的實現
    本節是一個以SOA+struts實現登錄的例子。新建一個web工程,EmployeeWebService,然后將上面生成的幾個類放入你的src目錄下面,是放整個目錄,別只放幾個類進去. 構建struts資源。創建struts的過程就不在這里細說了。創建的action內容如下:

    package com.ascent.webservice.struts.action;

    import javax.servlet.http.HttpServletRequest;

    import javax.servlet.http.HttpServletResponse;

    import javax.servlet.http.HttpSession;

    import org.apache.commons.logging.Log;

    import org.apache.commons.logging.LogFactory;

    import org.apache.struts.action.Action;

    import org.apache.struts.action.ActionForm;

    import org.apache.struts.action.ActionForward;

    import org.apache.struts.action.ActionMapping;

    import org.apache.struts.action.ActionMessage;

    import org.apache.struts.action.ActionMessages;

    import com.ascent.webservice.struts.form.LoginForm;

    import com.ascent.webservice.bean.jaws.LoginDao;

    import com.ascent.webservice.bean.jaws.LoginDaoServiceLocator;

    public class LoginAction extends Action

    {

    public ActionForward execute(ActionMapping mapping, ActionForm form,

    HttpServletRequest request, HttpServletResponse response)

    throws Exception {

    LoginForm loginForm = (LoginForm) form;

    String userName = loginForm.getLoginName();

    String password = loginForm.getPassword();

    LoginDaoServiceLocator loginDaoServiceLocator = new LoginDaoServiceLocator();

    LoginDao loginDao=

    loginDaoServiceLocator.getLoginDaoPort();

    boolean isExists = loginDao.isLogin(userName, password);

    if(isExists)

    {

    System.out.println("hello " + userName);

    HttpSession session = request.getSession();

    session.setAttribute("loginName", loginForm.getLoginName());

    return mapping.findForward("success");

    }

    else

    {

    System.out.println("sorry " + userName + ", you are not user in the system!");

    ActionMessages messages = new ActionMessages();

    messages.add("login",new ActionMessage("error.login.jsp.loginName.exists"));

    this.saveErrors(request, messages);

    return mapping.getInputForward();

    }

    }

    }

    這里用到的LoginDaoServiceLocator類和getLoginDaoPort()方法就是使用WSDL2Java命令把wsdl文件生成的類。現在就可以打包成叫employee的war文件,運行它。至此,你便可以在瀏覽器中輸入http://localhost:8080/employee/login.jsp,運行你這個SOA的應用了。如果是把服務器端部署到別的機器上,只要把localhost改為相應的ip就可以了。

    小結
    本章首先介紹了目前一個前沿技術:Web Services和面向服務的軟件架構(Service Oriented Architecture,簡稱SOA)。在理解了Web Services原理之后, 接下來我們講解了如何使用J2EE和EJB3.0來實現Web Services。

     

    posted @ 2011-08-31 13:25 Daniel 閱讀(6464) | 評論 (0)編輯 收藏

    rpc和document的區別  

    2008-10-24 18:48:07|  分類: webservice|字號 訂閱

     

    翻譯自 The Difference Between RPC and Document Style WSDL

    rpc和document到底是什么意思?他們如何同"encoded/literal"相關聯?二者的沖突來自哪里?

    binding的style和use

    通常情況下,web服務是由一個wadl文件來描述的的。在這個文件中,抽象的描述所支持的web服務操作和消息,然后關聯到一個具體的網

    絡歇息和消息格式。典型的wsdl文件由這個幾個元素組成:tyoes,message.porttype用來描述抽象的定義;biding和service指定具體的實

    現。所有的這些元素包裝在definitions元素中。

    在binding元素中,rpc和document是最值得我們關注的元素。wsdl 的 binding描述了如何將service關聯到消息協議;這些消息協議是

    http,mime,soap中的任意一種。不過,實際中,soap是最常用的協議;rpc和document的區別也是在soap的下的區別(it is SOAP that

    the RPC/document distinction refers to)。通常http(s)是傳輸soap消息的協議。

    <wsdl:binding>元素包含一對參數,style(rpc|document)和use(encoded|literal),他倆會影響到soap消息的格式.他們的使用方式如下

    <wsdl:binding name="Config1Binding" type="prt0:CreditLimitLocalWebServiceVi_Document">

        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />

         <wsdl:operation name="creditLimitCheck">

            <soap:operation soapAction="" />

             <wsdl:input>

                <soap:body use="literal" parts="parameters" />

             </wsdl:input>

            <wsdl:output>

                <soap:body use="literal" />

             </wsdl:output>

    </wsdl:operation>

    </wsdl:binding>

    style屬性:

    wsdl1.1要求binding的style要么是rpc要么是doucment。這個選擇與如何組織soap的負荷相關。下面是分別他們如何影響<soap:body>內

    容的細節。

    document:<soap:body>的內容由定義在<wsdl:type>中的xml模式指定。他不需要遵循特定的soap規范。簡言之,soap消息是通過

    <soap:body>中的document發送出去,而沒有額外的要遵循的格式規則。document style是一種默認的選擇。

    rpc: <soap:body>元素的結構需要遵循特定的規則(soap1.1規范第7部分有細節)。根據這些規則,<soap:body>可以包含唯一一個元素

    ,這個元素在operation后被命名,所有的參數都必須寫成這個元素的子元素。

    因為可以自由的選擇哪種消息格式,遵循document格式的soap消息看上去很像rpc格式。

    現在,決定性的問題是:選擇其中任意一個選項后的結果是什么樣?為何要選擇rpc或者document?很多情況下,選擇他們后,soap消息

    的格式看起來很相像,那么為什么還要提供這種選擇呢?原因要從soap標準化的歷程中尋找。

    use屬性

    這個屬性指定了soap消息的編碼規則。他同樣在<wsdl:binding>元素中完成。他的值為encoded或者literal.

    他引用了一系列規則,soap客戶端和soap服務端都遵循這些規則以解釋<body>元素中的內容。

    use="literal" 意味著type定義遵循xml模式定義

    use="encoded" 參考xml中已有的應用數據,通常指的是soap1.1規范中的soap編碼規則。如果文檔中沒有自定義數據,就可以選擇

    encoded。

    附文章全文:

    The Difference Between RPC and Document Style WSDL

    Summary

    RPC style and document style are catchphrases frequently heard in the context of web services and the SOAP protocol. What exactly do they mean? How do they relate to the pair of terms "encoded/literal"? Where does the confusion about these terms come from? This article describes the WSDL binding style and use attributes.

    By Susanne Rothaug

    20 Nov 2004

    Binding Style and Use

    A web service is usually described by a WSDL (Web Services Description Language) document. In this document, the supported web service operations and messages are described abstractly and then bound to a concrete network protocol and message format. A typical WSDL document consists of the following elements: "types," "message," and "portType" for the abstract definitions; "binding" and "service" for the concrete specification. All of these elements are wrapped inside a "definitions" element.

    In the context of RPC and document style, it is the binding element that we need to take a closer look at. A WSDL binding describes how the service is bound to a messaging protocol, either HTTP GET/POST, MIME, or SOAP. In practice, SOAP is the most universally used protocol; it is SOAP that the RPC/document distinction refers to. Usually HTTP(S) is used as transport protocol for the SOAP message – "SOAP over HTTP(S)."

    The <wsdl:binding> element of the WSDL contains a pair of parameters that influence the form of the resulting SOAP messages: binding style (RPC or document) and use (encoded or literal). See how style and use are defined in the WSDL fragment below: <wsdl:binding name="Config1Binding" type="prt0:CreditLimitLocalWebServiceVi_Document">     <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />      <wsdl:operation name="creditLimitCheck">         <soap:operation soapAction="" />          <wsdl:input>             <soap:body use="literal" parts="parameters" />          </wsdl:input>         <wsdl:output>             <soap:body use="literal" />          </wsdl:output> </wsdl:operation> </wsdl:binding>

    The "Style" Attribute

    WSDL 1.1 specifies the style of the binding as either RPC or document. This choice corresponds to how the SOAP payload - i.e., how the contents of the <soap:Body> element - can be structured. Here are some details of how each style affects the contents of <soap:Body>:

    • Document: the content of <soap:Body> is specified by XML Schema defined in the <wsdl:type> section. It does not need to follow specific SOAP conventions. In short, the SOAP message is sent as one "document" in the <soap:Body> element without additional formatting rules having to be considered. Document style is the default choice.

    • RPC: The structure of an RPC style <soap:Body> element needs to comply with the rules specified in detail in Section 7 of the SOAP 1.1 specification. According to these rules, <soap:Body> may contain only one element that is named after the operation, and all parameters must be represented as sub-elements of this wrapper element.

    As a consequence of the freedom of choice that the document style offers, the SOAP messages conforming to a document style WSDL may look exactly the same as the RPC equivalent.

    The decisive question now is: What are the consequences of choosing one option or another? Why choose RPC over document, or document over RPC? In many cases, the SOAP messages generated from either RPC or document style WSDLs look exactly the same - so why offer the choice at all? The reason may be found in the history of the SOAP standard.

    SOAP has its roots in synchronous remote procedure calls over HTTP and the appearance of the document accordingly followed these conventions. Later, it was seen as a simplification to use arbitrary XML in the SOAP body without adhering to conventions. This preference is reflected in the document style WSDL documents. So far, both options are represented in the WSDL specification and the choice of one or the other is mainly a question of personal taste since most SOAP clients today accept both versions.

    The "Use" Attribute

    The use attribute specifies the encoding rules of the SOAP message. This is also done within the <wsdl:binding> element, as seen in the example above. The value can be encoded or literal. It refers to the serialization rules followed by the SOAP client and the SOAP server to interpret the contents of the <Body> element in the SOAP payload.

    • use="literal" means that the type definitions literally follow an XML schema definition.

    • use="encoded" refers to the representation of application data in XML, usually according to the SOAP encoding rules of the SOAP 1.1 specification. The rules to encode and interpret a SOAP body are in a URL specified by the encodingStyle attribute. Encoded is the appropriate choice where non-treelike structures are concerned, because all others can be perfectly described in XML Schema.

    The combination of the style and use attributes leads to four possible style/use pairs:

    1. RPC/encoded

    2. RPC/literal

    3. document/encoded

    4. document/literal

    Some of these combinations are rarely used in practice, such as document/encoded. In general, the literal use is gaining importance, and as far as RPC/encoded is concerned, the Web Services Interoperability Organization (WS-I) in its Basic Profile Version 1.0a of August 2003 ruled out the use of SOAP encoding with web services. Document/literal and RPC/literal will be the only allowed style/use combinations in the future.

    posted @ 2011-08-31 13:24 Daniel 閱讀(1231) | 評論 (0)編輯 收藏
      2011年5月27日

    The project requirements: Should convert TIF into JPG with JAVA, But seems only IMAGEIO can't support TIF.
    So should use JAI, Java Advantage Image jars.

    JAI api download page: http://download.java.net/media/jai/builds/release/1_1_3/

    J
    AI API document page: http://download.oracle.com/docs/cd/E17802_01/products/products/java-media/jai/forDevelopers/jai-apidocs/index.html



    C
    ode as below:
    package com.ebay.test1;
    import java.awt.image.RenderedImage;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import javax.imageio.ImageIO;
    import javax.media.jai.JAI;
    import javax.media.jai.RenderedOp;
    import org.junit.Test;
    import com.sun.media.jai.codec.ImageCodec;
    import com.sun.media.jai.codec.ImageDecoder;
    import com.sun.media.jai.codec.ImageEncoder;
    import com.sun.media.jai.codec.JPEGEncodeParam;
    public class Snippet {
    @Test
    public void testTif2JPG2(){
    try{
    String filename =  "F:\\Downloads\\CCITT_2.TIF";
    String jpegFileName = "F:\\Downloads\\CCITT_2.JPG";
    RenderedOp source = JAI.create("fileload", filename);
    FileOutputStream stream = null;
    stream = new FileOutputStream(jpegFileName );
    com.sun.media.jai.codec.JPEGEncodeParam JPEGparam = new
    com.sun.media.jai.codec.JPEGEncodeParam();
    ImageEncoder encoder =
    ImageCodec.createImageEncoder("jpeg",stream,JPEGparam);
    encoder.encode(source);
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    @Test
    public void testTif2JPG3(){
    try{
    String filename =  "F:\\Downloads\\CCITT_2.TIF";
    String jpegFileName = "F:\\Downloads\\CCITT_2.JPG";
    FileOutputStream stream = null;
    stream = new FileOutputStream(jpegFileName );
    byte[] b = getBytesFromFile(new File(filename));
    InputStream bais = new ByteArrayInputStream(b);
    ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", bais, null);
                RenderedImage ri = decoder.decodeAsRenderedImage();
    // RenderedOp source = JAI.create("TIFF", bais);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    JPEGEncodeParam JPEGparam = new JPEGEncodeParam();
    JPEGparam.setQuality(Float.MIN_VALUE);
    ImageEncoder encoder = ImageCodec.createImageEncoder("jpeg",baos,JPEGparam);
    encoder.encode(ri);
    byte[] byteArray = baos.toByteArray();
    FileOutputStream fos = new FileOutputStream(new File(jpegFileName));
    fos.write(byteArray);
    }catch(Exception e){
    e.printStackTrace();
    }
    }
     @Test
    public void testTif2JPG4(){
    try{
    String filename =  "F:\\Downloads\\CCITT_2.TIF";
    String jpegFileName = "F:\\Downloads\\CCITT_2.JPG";
    FileOutputStream stream = null;
    stream = new FileOutputStream(jpegFileName );
    byte[] b = getBytesFromFile(new File(filename));
    InputStream bais = new ByteArrayInputStream(b);
                ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", bais, null);
                RenderedImage ri = decoder.decodeAsRenderedImage();
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ImageIO.write(ri, "JPEG", outputStream);
                stream.write(outputStream.toByteArray());
    }catch(Exception e){
    e.printStackTrace();
    }
    }
    // Returns the contents of the file in a byte array.
        public static byte[] getBytesFromFile(File file) throws IOException {
            InputStream is = new FileInputStream(file);
        
            // Get the size of the file
            long length = file.length();
        
            // You cannot create an array using a long type.
            // It needs to be an int type.
            // Before converting to an int type, check
            // to ensure that file is not larger than Integer.MAX_VALUE.
            if (length > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("File is too big, can't support.");
            }
        
            // Create the byte array to hold the data
            byte[] bytes = new byte[(int)length];
        
            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset < bytes.length
                   && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
                offset += numRead;
            }
        
            // Ensure all the bytes have been read in
            if (offset < bytes.length) {
                throw new IOException("Could not completely read file "+file.getName());
            }
        
            // Close the input stream and return bytes
            is.close();
            return bytes;
        }
    }
    posted @ 2011-05-27 22:39 Daniel 閱讀(2204) | 評論 (0)編輯 收藏
    僅列出標題  
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(3)

    隨筆檔案

    文章分類

    文章檔案

    相冊

    搜索

    •  

    最新評論

    主站蜘蛛池模板: 国产精品久久久久久久久久免费| 免费看国产精品3a黄的视频| 亚洲大片在线观看| 99精品视频在线视频免费观看| 亚洲宅男天堂a在线| 蜜桃视频在线观看免费网址入口| 国产成人亚洲综合无| 亚洲日韩精品A∨片无码| 亚欧免费视频一区二区三区| 久久久久亚洲AV无码去区首| 中文字幕亚洲天堂| 99视频在线精品免费| 亚洲AV无码国产剧情| 亚洲AV无码1区2区久久| 综合在线免费视频| 亚洲视频在线免费| 亚洲国产成人精品无码区在线秒播| 国产人妖ts在线观看免费视频| 免费观看一区二区三区| 亚洲香蕉久久一区二区 | 好湿好大好紧好爽免费视频| 久久精品国产精品亚洲艾| 日韩高清在线免费观看| 久久综合九色综合97免费下载| 国产成人亚洲精品播放器下载| 亚洲一区中文字幕久久| 亚洲福利视频一区二区| 97人妻无码一区二区精品免费| 亚洲五月午夜免费在线视频| 亚洲男人的天堂久久精品| 亚洲欧洲久久久精品| 女人被免费视频网站| 久久久精品2019免费观看| h在线看免费视频网站男男| 亚洲中文字幕一二三四区| 久久亚洲AV午夜福利精品一区 | 国拍在线精品视频免费观看 | 巨胸喷奶水www永久免费 | 在线观看亚洲专区| 亚洲国产亚洲综合在线尤物| 国产成人精品日本亚洲网站|