Wednesday, May 29, 2013

KM: JEE Series - การประยุกต์ใช้ Web Service Handler Chain


อะไรคือ Web Service Handler Chain

มันคือ interceptor pattern ที่ถูก implement ใน Web Service ทำหน้าที่ในการดักจับ request / response ระหว่าง client - service provider ดังนั้นจะมี 2 ขา คือ inbound message และ outbound message หากพิจารณาจาก client การเรียกไปยัง service provider จะเป็น outbound message และสิ่งที่ได้รับกลับมาจาก service provider ก็จะเป็น inbound message ในทางกลับกันถ้าพิจารณาจาก service provider การเรียกเข้ามาจะเป็น inbound message และสิ่งที่ตอบกลับไปจะเป็น outbound message


การนำไปประยุกต์ใช้งาน

  • client / service provider: การทำ logging facade ข้อดีคือเป็นการ decoupling code ในส่วนการทำ logging และ business logic ออกจากกัน ปัญหาที่มักจะพบบ่อยในการใช้ tool เพื่อ generate client library มาใช้งานคือหาก request / response ที่ส่งมาไม่สามารถทำ xml binding ได้ จะไม่สามารถตรวจสอบว่าค่าไหนที่ไม่ถูกต้อง วิธีนึงคือ log message เพือตรวจสอบว่าส่งอะไรเข้ามา (แต่มีข้อแม้คือต้อง well-form)
  • service provider: การทำ customize authentication / authorization อันที่จริงใน web service รองรับ WS-Security อยู่แล้ว แต่หาก requirement ในการทำ AA มันซับซ้อนก็สามารถ implement ได้ใน layer ของ Chain Handler
  • client: การทำ customize soap header สามารถเพิ่ม soap element ใน soap header ได้ขึ้นกับ service provider specification

ข้อจำกัดในการพัฒนา


  1. ตัวอย่างจะใช้ oracle weblogic workshop 10.3 ดังนั้นจะมี bundle jar library ในการพัฒนา JEE อยู่แล้ว หากพบว่าขาด library ตัวไหนก็ลองหา download มาเอง
  2. ตัวอย่างไม่รวมในส่วนการ generate stub code ซึ่งในที่นี้จะใช้ Web Service version 2.0 แบบ jax-ws อาจต้องมีการเปลี่ยนแปลง package นิดหน่อย แต่ concept โดยรวมเหมือนกัน


ขั้นตอนการพัฒนา

1. สร้าง Web Service SOAPHandler ขึ้นมา โดย method ที่ต้องไป implement หลักๆ จะเป็น handleMessage(SOAPMessageContext context) ซึ่งจะเป็น interceptor เวลามี inbound / outbound message ในที่นี้จะทำตัวอย่าง logging facade จึงสร้าง constructor โดยส่ง logger object มา

 
 import javax.xml.soap.Name;  
 import javax.xml.soap.SOAPElement;  
 import javax.xml.soap.SOAPEnvelope;  
 import javax.xml.soap.SOAPHeader;  
 import javax.xml.soap.SOAPHeaderElement;  
 import javax.xml.soap.SOAPMessage;  
 import javax.xml.soap.SOAPPart;  
 import javax.xml.ws.handler.MessageContext;  
 import javax.xml.ws.handler.soap.SOAPHandler;  
 import javax.xml.ws.handler.soap.SOAPMessageContext;  
 import org.apache.log4j.Logger;  
 public class IBSOAPHandler implements SOAPHandler<SOAPMessageContext> {  
      private Logger logger = null;  
      public IBSOAPHandler(Logger logger)  
      {  
           super();  
           this.logger = logger;   
      }  
      @Override  
      public Set getHeaders() {  
           return null;  
      }  
      @Override  
      public void close(MessageContext context) {  
      }  
      @Override  
      public boolean handleFault(SOAPMessageContext context) {  
           return false;  
      }  
      @Override  
      public boolean handleMessage(SOAPMessageContext context) {  
     return true;  
      }  
 }  

2. ทำการ check message direction
        Boolean outboundProperty =
            (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (outboundProperty.booleanValue()) {
            logger.info("Outbound message: ");
        }
        else
        {
            logger.info("Inbound message: ");
        }

3. อ่านค่า SOAP Message จาก SOAP Context
         SOAPMessage m = context.getMessage();

4. แปลงค่า SOAP Message ให้เป็น String เพื่อส่งไปยัง log4j ให้เขียนลง file
 private String printSource(SOAPMessage message)
 {
  try 
  {
   ByteArrayOutputStream baos = new ByteArrayOutputStream();

   message.writeTo(baos);
   
   String soapMessage = baos.toString(); 
   
   baos.close();
   
   return soapMessage;
  } 
  catch (Exception e) 
  {
   logger.error("Error while print soap message", e);
  }
  
  return null;
 }

5. หน้าตาก็จะออกมาแบบนี้
 import javax.xml.soap.SOAPElement;  
 import javax.xml.soap.SOAPEnvelope;  
 import javax.xml.soap.SOAPHeader;  
 import javax.xml.soap.SOAPHeaderElement;  
 import javax.xml.soap.SOAPMessage;  
 import javax.xml.soap.SOAPPart;  
 import javax.xml.ws.handler.MessageContext;  
 import javax.xml.ws.handler.soap.SOAPHandler;  
 import javax.xml.ws.handler.soap.SOAPMessageContext;  
 import org.apache.log4j.Logger;  
 public class IBSOAPHandler implements SOAPHandler<SOAPMessageContext> {  
      private Logger logger = null;  
      public IBSOAPHandler(Logger logger)  
      {  
           super();  
           this.logger = logger;   
      }  
      @Override  
      public Set getHeaders() {  
           return null;  
      }  
      @Override  
      public void close(MessageContext context) {  
      }  
      @Override  
      public boolean handleFault(SOAPMessageContext context) {  
           return false;  
      }  
      @Override  
      public boolean handleMessage(SOAPMessageContext context) {  
     Boolean outboundProperty =  
       (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);  
        SOAPMessage m = context.getMessage();  
     if (outboundProperty.booleanValue()) {  
          logger.info("Outbound message: " + this.printSource(m));  
     }  
     else  
     {  
       logger.info("Inbound message: " + this.printSource(m));  
     }  
     return true;  
      }  
      private String printSource(SOAPMessage message)  
      {  
           try   
           {  
                ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                message.writeTo(baos);  
                String soapMessage = baos.toString();   
                baos.close();  
                return soapMessage;  
           }   
           catch (Exception e)   
           {  
                logger.error("Error while print soap message", e);  
           }  
           return null;  
      }  
 }  

Reference

Note

การทำงานในลักษณะ interceptor ที่พบบ่อยอีกอันคือ servlet filter และพวก spring aop ดังนั้นการประยุกต์ใช้งานจึงคล้ายๆ กัน

No comments:

Post a Comment