900 likes | 1.18k Views
J2EE 1.4 APIs For Web Services. Michael D. Thomas mdthomas@ibiblio.org. Agenda. Quick Tour Of J2EE APIs Service Oriented Architecture SOAP & WSDL: The 10 minute guide JAX-RPC SAAJ JAXP JAXR. A Simple Example. Company has web app that looks up invoices The customers like it
E N D
J2EE 1.4 APIs For Web Services Michael D. Thomas mdthomas@ibiblio.org
Agenda • Quick Tour Of J2EE APIs • Service Oriented Architecture • SOAP & WSDL: The 10 minute guide • JAX-RPC • SAAJ • JAXP • JAXR
A Simple Example • Company has web app that looks up invoices • The customers like it • Customer wants to pass the data automatically to another application • Typical situation for a web service • Will do it with: • Straight XML (JAXP) • SAAJ: Wrapper around SOAP and HTTP • JAX-RPC: Uses SOAP and WSDL, but you never have to see it
Invoice JSP <html> <head> <title>Invoice</title> </head> <body bgcolor="white"> <%@ page language="java" import="org.ibiblio.mdthomas.ws.autopartssupplier.*" %> <jsp:useBean id="invoiceHelper" scope="request" class="org.ibiblio.mdthomas.ws.autopartssupplier.InvoiceHelper"/> <jsp:setProperty name=”invoiceHelper” property=”*”/> <% InvoiceBean invoice = invoiceHelper.getInvoiceById(); CustomerBean customer = invoice.getCustomer(); InvoiceItemBean items[] = invoice.getInvoiceItems(); %> Continued . . . .
Invoice JSP <% for (int i=0; i<items.length;i++) { %> <tr> <td><%= items[i].getName() %></td> <td><%= items[i].getProductId() %></td> <td><%= items[i].getQuantity() %></td> <td align="right"> <%= items[i].getPerItemPrice() %> </td> <td align="right"> <%= items[i].getTotalLineItemPrice() %> </td> </tr> <% } %> Continued . . . .
Invoice Helper package org.ibiblio.mdthomas.ws.autopartssupplier; import java.rmi.Remote; import java.rmi.RemoteException; public class InvoiceHelper { long id; InvoiceBusinessDelegate invoiceBusinessDelegate; public InvoiceHelper () { invoiceBusinessDelegate = BusinessDelegateFactory.getInvoiceBusinessDelegate();} public void setInvoiceId (long id) { this.id = id; } public long getInvoiceId() { return id;} public InvoiceBean getInvoiceById() { return invoiceBusinessDelegate.getInvoiceById(this.id);} public InvoiceBean getInvoiceById(long id) { return invoiceBusinessDelegate.getInvoiceById(id);} }
InvoiceBean (incomplete) package org.ibiblio.mdthomas.ws.autopartssupplier; public class InvoiceBean implements Serializable { private CustomerBean customer; private InvoiceItemBean invoiceItems[]; private long invoiceId; private float total; private float shippingCharge; private float subTotal; public InvoiceBean() {} public float getTotal() { return total;} public void setTotal(float total) { this.total = total;} public void setCustomer(CustomerBean customer) { this.customer = customer;} public CustomerBean getCustomer() { return customer; } public InvoiceItemBean[] getInvoiceItems() { return invoiceItems;} public void setInvoiceItems(InvoiceItemBean items[]) { this.invoiceItems = items;} }
XML Invoice JSP <?xml version="1.0"?> <ordering:invoice xmlns:ordering="http://www.tinasautoparts.com/xmlns/orders"> <%@ page language="java" import="org.ibiblio.mdthomas.ws.autopartssupplier.*" %> <jsp:useBean id="invoiceHelper" scope="request" class="org.ibiblio.mdthomas.ws.autopartssupplier.InvoiceHelper" /> <jsp:setProperty name="invoiceHelper" property="*"/> <% InvoiceBean invoice = invoiceHelper.getInvoiceById(); CustomerBean customer = invoice.getCustomer(); InvoiceItemBean items[] = invoice.getInvoiceItems(); %> <ordering:invoiceId> <%= invoice.getInvoiceId() %> </ordering:invoiceId> Continued . . .
“Web Services” Requestor public static void main(String[] args) throws Exception { long invoiceId = 0; if (args.length>0) { invoiceId = Long.parseLong(args[0]);} // Create the URL and encode the invoiceId param String base = "http://localhost:8080/jsp-examples/ch03/invoiceAsXml.jsp?"; String invoiceParam = "invoiceId="+invoiceId+"&"; URL invoiceURL = new URL(base+invoiceParam); // Connect to the URL URLConnection conn = invoiceURL.openConnection(); conn.connect(); // Get the result and create the XML InputStream invoiceStream = conn.getInputStream(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(invoiceStream); // Handle the XML handleXML(doc); }
“Web Services” Requestor private static void handleXML(Document doc) { float invoiceTotal = 0f; int totalQuantities = 0; float shippingCharge =0f; Element docRoot = doc.getDocumentElement(); NodeList children = docRoot.getChildNodes(); for (int i=0;i<children.getLength();i++) { Node node = children.item(i); System.out.println("node name: "+node.getNodeName()); short nodeType = node.getNodeType(); if (node.getNodeName().equals("ordering:customer")) { Element customerElement = (Element)node; NodeList nodeList = customerElement.getElementsByTagName("ordering:customerName"); Element customerNameElem = (Element)nodeList.item(0); Node customerNameText = customerNameElem.getFirstChild(); String customerName = customerNameText.getNodeValue(); System.out.println("Customer name: "+customerName); } Continued . . .
Basic XML Web Services Issues • Satisfies the business requirement • Not standards based – doesn’t use SOAP or WSDL • Had to handle HTTP directly
SAAJ & SOAP Approach • Uses SOAP standard • Similar to previous example, but requestor handles SOAP details • SOAP: data (Body) and meta-data (Header) • Contained in a SOAP envelope • Can attach data outside of the SOAP envelope • SAAJ: handles HTTP requests, wraps JAXP in SOAP specific nature (like JDOM)
SOAP Request POST /invoice-jaxrpc/invoice HTTP/1.1 Content-Type: text/xml; charset="utf-8" Content-Length: 246 SOAPAction: "" Cache-Control: no-cache Pragma: no-cache User-Agent: Java/1.4.2 Host: localhost:9090 Accept: text/html, text/xml, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <getInvoiceById xmlns="http://www.ibiblio.org/mdthomas/invoice/types"> <long_1 xmlns="">9845</long_1> </getInvoiceById> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
SOAP Request POST /invoice-jaxrpc/invoice HTTP/1.1 Content-Type: text/xml; charset="utf-8" Content-Length: 246 SOAPAction: "" Cache-Control: no-cache Pragma: no-cache User-Agent: Java/1.4.2 Host: localhost:9090 Accept: text/html, text/xml, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <getInvoiceById xmlns="http://www.ibiblio.org/mdthomas/invoice/types"> <long_1 xmlns="">9845</long_1> </getInvoiceById> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
SOAP Request POST /invoice-jaxrpc/invoice HTTP/1.1 Content-Type: text/xml; charset="utf-8" Content-Length: 246 SOAPAction: "" Cache-Control: no-cache Pragma: no-cache User-Agent: Java/1.4.2 Host: localhost:9090 Accept: text/html, text/xml, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <getInvoiceById xmlns="http://www.ibiblio.org/mdthomas/invoice/types"> <long_1 xmlns="">9845</long_1> </getInvoiceById> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
SOAP Response (incomplete) HTTP/1.1 200 OK X-Powered-By: Servlet/2.4 SOAPAction: "" Content-Type: text/xml; charset="utf-8" Transfer-Encoding: chunked Date: Sun, 23 Nov 2003 16:00:17 GMT Server: Sun-Java-System/JWSDP-1.3 <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns0="http://www.ibiblio.org/mdthomas/invoice/types"> <env:Body> <ns0:getInvoiceByIdResponse> <result> <customer> data </customer> <invoiceId>9845</invoiceId> <invoiceItem> data </invoiceItem> continued…
SAAJ Provider public class InvoiceSAAJProvider extends HttpServlet { MessageFactory messageFactory; public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/xml"); response.setBufferSize(8192); OutputStream out = response.getOutputStream(); try { // initialize the messageFactory messageFactory = MessageFactory.newInstance(); // Get the requested invoice long invoiceId = getInvoiceId(request); InvoiceHelper invoiceHelper = new InvoiceHelper(); InvoiceBean invoice = invoiceHelper.getInvoiceById(invoiceId); // Create the SOAP output message SOAPMessage outputMessage = createOutputSOAPMessage(invoice); // Write the output SOAP message to the response output stream outputMessage.writeTo(out); } catch (Exception ex) { ex.printStackTrace(); } out.close(); }
SAAJ Provider private long getInvoiceId(HttpServletRequest request) throws SOAPException, IOException { // create the input SOAP message from the request's inputStream MimeHeaders mimeHeaders = getMimeHeaders(request); InputStream inputStream = request.getInputStream(); SOAPMessage inputMessage = messageFactory.createMessage(mimeHeaders,inputStream); // get the SOAP body SOAPPart inputPart = inputMessage.getSOAPPart(); SOAPEnvelope inputEnvelope = inputPart.getEnvelope(); SOAPBody inputBody = inputMessage.getSOAPBody(); // get the invoice id as a String Name getInvName = inputEnvelope.createName("getInvoiceById", null, "http://www.ibiblio.org/mdthomas/invoice/types"); Iterator it = inputBody.getChildElements(getInvName); SOAPElement getInvoiceElem = (SOAPElement)it.next(); Name idName = inputEnvelope.createName("long_1")); it = getInvoiceElem.getChildElements(idName); SOAPElement invoiceIdElem = (SOAPElement)it.next(); // convert to a long and return String invoiceIdStr = invoiceIdElem.getValue(); long invoiceId = Long.parseLong(invoiceIdStr); return invoiceId; }
Creating output private SOAPMessage createOutputSOAPMessage(InvoiceBean invoice) throws SOAPException { // Create a message SOAPMessage outputMessage = messageFactory.createMessage(); SOAPPart outputSoapPart = outputMessage.getSOAPPart(); SOAPEnvelope outputEnvelope = outputSoapPart.getEnvelope(); outputEnvelope.addNamespaceDeclaration("ns0", "http://www.ibiblio.org/mdthomas/invoice/types"); // Get the SOAP header from the message and remove it SOAPHeader header = outputMessage.getSOAPHeader(); header.detachNode(); // Get the SOAP body from the message SOAPBody body = outputMessage.getSOAPBody(); // Add the top level elements Name responseName = outputEnvelope.createName( "getInvoiceByIdResponse", "ns0", null); SOAPElement invoiceElem = body.addBodyElement(responseName); SOAPElement resultElem = invoiceElem.addChildElement("result"); // add the invoice id SOAPElement invoiceIdElem = resultElem.addChildElement("invoiceId"); long invoiceId = invoice.getInvoiceId(); invoiceIdElem.addTextNode(String.valueOf(invoiceId)); // add customer CustomerBean customer = invoice.getCustomer(); SOAPElement customerElem = resultElem.addChildElement("customer"); SOAPElement customerNameElem = customerElem.addChildElement("name");
Creating output (incomplete) //add invoice items InvoiceItemBean items[] = invoice.getInvoiceItems(); for (int i=0;i<items.length;i++) { SOAPElement invoiceItemElem = resultElem.addChildElement("invoiceItems"); SOAPElement nameElem = invoiceItemElem.addChildElement("name"); String name = items[i].getName(); nameElem.addTextNode(name); SOAPElement perItemPriceElem = invoiceItemElem.addChildElement("perItemPrice"); float perItemPrice = items[i].getPerItemPrice(); perItemPriceElem.addTextNode(String.valueOf(perItemPrice)); SOAPElement totalLineItemPriceElem = invoiceItemElem.addChildElement( "totalLineItemPrice"); float totalLineItemPrice = items[i].getTotalLineItemPrice(); totalLineItemPriceElem.addTextNode( String.valueOf(totalLineItemPrice)); SOAPElement quantityElem = invoiceItemElem.addChildElement("quantity"); int qty = items[i].getQuantity(); quantityElem.addTextNode(String.valueOf(qty)); }
Creating output (incomplete) // save and return outputMessage.saveChanges(); return outputMessage; }
SAAJ Requestor public static SOAPMessage createSOAPRequest(long invoiceId) throws SOAPException { // Create message factory MessageFactory messageFactory = MessageFactory.newInstance(); // Create a message SOAPMessage message = messageFactory.createMessage(); SOAPPart soapPart = message.getSOAPPart(); SOAPEnvelope envelope = soapPart.getEnvelope();
SAAJ Requestor // Get the SOAP header from the message and remove it SOAPHeader header = message.getSOAPHeader(); header.detachNode(); // Get the SOAP body from the message SOAPBody soapBody = message.getSOAPBody(); // create the outer invoice element Name getInvoiceName = envelope.createName("getInvoiceById", null, "http://ibiblio.org/invoice/types"); SOAPBodyElement getInvoiceElem = soapBody.addBodyElement(getInvoiceName);
SAAJ Requestor // create the invoice parameter Name invoiceIdName = envelope.createName("long_1",null,""); SOAPElement invoiceIdElem = getInvoiceElem.addChildElement(invoiceIdName); invoiceIdElem.addNamespaceDeclaration("",""); invoiceIdElem.addTextNode(String.valueOf(invoiceId)); message.saveChanges(); return message; }
SAAJ Code Problems • Compliant with SOAP… • Still haven’t done anything with WSDL • Lots and lots of XML creation and parsing • Absolutely no error checking – not even simple type checking • Have to handle all interoperability problems
JAX-RPC Service Endpoint Interface package org.ibiblio.mdthomas.ws.autopartssupplier; import java.rmi.Remote; import java.rmi.RemoteException; public interface InvoiceWebService extends Remote { public InvoiceBean getInvoiceById(long id) throws RemoteException; }
JAX-RPC Servant public class InvoiceHelper implements InvoiceWebService { // existingcode }
Run the tools • SEI-to-WSDL tool and WSDL-to-SEI tool are required by JAX-RPC specification • Wscompile and wsdeploy with the Java Web Services Development Pack from Sun • Java2WSDL with Apache Axis
JAX-RPC Requestor public static void main(String[] args) { try { // Get the web service port InvoiceWebService_Service wsDef = new InvoiceWebService_Service_Impl(); InvoiceWebService_PortType stub = wsDef.getInvoiceWebServicePort(); // Create the input parameter GetInvoiceById idWrapper = new GetInvoiceById(1000); // Make the remote call to the web service GetInvoiceByIdResponse invoiceWrapper = stub.getInvoiceById(idWrapper); // Unwrap the result InvoiceBean invoice = invoiceWrapper.getResult(); // Print some data CustomerBean customer = invoice.getCustomer(); System.out.println("Customer: "+customer.getName()); InvoiceItemBean items[] = invoice.getInvoiceItems(); for (int i=0;i<items.length;i++) { System.out.println("item name: "+items[i].getName()); } } catch (Exception ex) { ex.printStackTrace(); } }
JAX-RPC Requestor public static void main(String[] args) { try { // Get the web service port InvoiceWebService_Service wsDef = new InvoiceWebService_Service_Impl(); InvoiceWebService_PortType stub = wsDef.getInvoiceWebServicePort(); // Create the input parameter GetInvoiceById idWrapper = new GetInvoiceById(1000); // Make the remote call to the web service GetInvoiceByIdResponse invoiceWrapper = stub.getInvoiceById(idWrapper); // Unwrap the result InvoiceBean invoice = invoiceWrapper.getResult(); // Print some data CustomerBean customer = invoice.getCustomer(); System.out.println("Customer: "+customer.getName()); InvoiceItemBean items[] = invoice.getInvoiceItems(); for (int i=0;i<items.length;i++) { System.out.println("item name: "+items[i].getName()); } } catch (Exception ex) { ex.printStackTrace(); } }
.NET C# Requestor • Generate C# requestor based on WSDL: >wsdl.exe /language:CS /protocol:SOAP \ http://localhost:8080/invoice/jaxrpc/invoice?WSDL • Entry point: public getInvoiceByIdResponse getInvoiceById( [System.Xml.Serialization.XmlElementAttribute("getInvoiceById", Namespace="http://www.ibiblio.org/mdthomas/invoice/types")] getInvoiceById getInvoiceById1) { object[] results = this.Invoke("getInvoiceById", new object[] {getInvoiceById1}); return ((getInvoiceByIdResponse)(results[0])); }
.NET C# Requestor class JAXClient { [STAThread] static void Main(string[] args) { // Create the stub InvoiceWebService service = new InvoiceWebService(); // Set the endpoint if (args.Length == 1) service.Url = args[0]; else service.Url = "http://localhost:8080/invoice-jaxrpc/invoice"; // create and send the request getInvoiceById idWrapper = new getInvoiceById(); idWrapper.long_1 = 1000; getInvoiceByIdResponse responseWrapper = service.getInvoiceById(idWrapper); // Receive and process the result InvoiceBean invoice = responseWrapper.result; Console.WriteLine("Invoice id: "+invoice.invoiceId); CustomerBean customer = invoice.customer; Console.WriteLine("Customer: "+customer.name); for (int i=0;i<invoice.invoiceItems.Length;i++) { Console.WriteLine("item name: "+invoice.invoiceItems[i].name); } } }
Web Services Architecture • Web Services as a presentation layer technology • Service Oriented Architecture (SOA) & Web Services • Message Exchange Patterns (MEPs) • RPC centric vs. Document centric approaches • Relevant SOAP & WSDL details
Fallacies Of Distributed Computing (Peter Deutch) • The network is reliable • Latency is zero • Bandwidth is infinite • The network is secure • Topology doesn’t change • There is one administrator • Transport cost is zero • The network is homogeneous
Waldo On Distributed Computing “…work in distributed object-oriented systems that is based on a model that ignores or denies [the differences between local and remote objects] is doomed to failure, and could easily lead to an industry-wide rejection of the notion of distributed object-based systems." -- Jim Waldo et al, “A Note on Distributed Computing,” 1994
Local objects are different than remote objects • Local objects are different than remote objects because: • Different address spaces (by reference vs. by copy) • Latency • Partial failure • Concurrency
Service Oriented Architecture (SOA) • Services have network interfaces • Service providers and consumers are loosely coupled • Interfaces are coarse grained • Location is transparent • Services can be looked up in a registry • Consumer can bind to a provider at runtime
SOA applied to EJBs • EJBs do distributed computing • J2EE patterns that promote SOA tenets: • Session Façade pattern with Value Objects for “bind” and “execute” • Service Locator pattern for “find”
Message Exchange Patterns • Message Exchange Patterns (MEPs): how messages are exchanged • There are really two: • Request-response • One way • WSDL defines two others, notification and solicit response, which aren’t supported by J2EE and aren’t widely used • Code to MEPs, not to the underlying protocol