อะไรคือ 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
ข้อจำกัดในการพัฒนา
- ตัวอย่างจะใช้ oracle weblogic workshop 10.3 ดังนั้นจะมี bundle jar library ในการพัฒนา JEE อยู่แล้ว หากพบว่าขาด library ตัวไหนก็ลองหา download มาเอง
- ตัวอย่างไม่รวมในส่วนการ 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 ContextSOAPMessage 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 ดังนั้นการประยุกต์ใช้งานจึงคล้ายๆ กัน