Java Web services for various client types

Use attachments from Java, .Net, and browser clients for FOP Web services

Successful deployment of Java-based Web services requires detailed knowledge and understanding of fundamental concepts behind Web services, different Simple Object Access Protocol (SOAP) implementations, XML-related technologies, SOAP Fault monitoring, and finally, clients using the Web service.

In this article, I discuss Java-based Web services' design and code for different client types. I cover Axis and .Net implementations and briefly discuss the architecture of both implementations. You should already be familiar with Apache Struts.

Note: You can download this article's source code from Resources.

FOP Web services

Apache's Formatting Objects Processor (FOP) is a print formatter powered by Extensible Stylesheet Language formatting objects (XSL-FO) and an output independent formatter. FOP is a Java application that reads a formatting object tree and renders the resulting pages to a specified output. XSL FOP supports PDF, PCL, PS, SVG, XML, Print, AWT, MIF, and TXT output formats. Browser-, Java-, or .Net-based clients send attachments (as static XSL-FO documents or XML and XSL documents) to an FOP Web service, which renders inputs and responds to the user in the desired output format (e.g., PDF). These attachments are part of a SOAP message. The SOAP standard and most of its Java implementations support MIME (Multipurpose Internet Mail Extensions) attachments. Microsoft's .Net platform supports DIME (Direct Internet Message Encapsulation) attachments only. Apache's Axis SOAP implementation supports MIME as well as DIME attachments. The FOP Web service described in this article covers interoperability issues between Apache Axis for Java and Web Services Enhancements (WSE) 1.0 for Microsoft .Net. First, here's an FOP Web services overview.

FOP Web services are based on the Java platform and use Apache Axis as the SOAP engine. You can install Axis on Java-based Web servers, such as Apache Tomcat, IBM WebSphere, etc. The .Net client uses Microsoft's WSE implementation, and custom filters handle exceptions and SOAP Fault elements. The FOP Web services implementation uses Axis's custom handlers to process attachments prior to invoking XSL-FOP logic and processes responses that might contain SOAP Faults. The custom filters and handlers let you scale the Web services and reuse SOAP Faults and attachment-processing logic. Axis lets you wrap Java exceptions with SOAP Faults. I discuss Axis custom handlers and WSE custom filters later in the article.

Browser-, Java-, and .Net-based clients can invoke FOP Web services. Figure 1 displays an FOP Web service and its .Net (C#, Visual Basic, etc.) client environment. Figure 2 shows an FOP Web service with browser- and Java-based clients. These figures illustrate key differences in how clients invoke FOP Web services. The .Net client directly invokes FOP Web services. The browser-based client invokes FOP Web services using JavaServer Pages (JSP), Struts, Façade, and FOPClient. The Java client invokes FOP Web services using Façade and FOPClient.

The .Net client (e.g., user) invokes a Windows program, FOPClient.exe, which in turn reads attachments from a local directory. The browser-based client also reads attachments from a local directory and then invokes a URL for the FOP Web service. The attachments are inputs to the FOP Web services' FOPService class and are rendered to the XSL-FO processor to perform transformation to the desired output format. The FOPService class then sends a response message to the client. The response message can be a SOAP Fault or attachment (e.g., PDF) that you can store in a local directory.

Figure 1. .Net client FOP Web service. Click on thumbnail to view full-size image.
Figure 2. Browser (with Struts integration) and Java client FOP Web service. Click on thumbnail to view full-size image.

Apache Axis architecture overview

In this section, I briefly discuss how the Axis engine works. Axis's primary responsibility is to invoke a series of handlers and process SOAP messages. Axis uses Message objects to wrap SOAP request, response, and fault messages. These Message objects are placed into MessageContext objects, and become available to all Axis implementation components. So the MessageContext object is a container for request, response, and fault messages and also offers other information, such as target service name, transport protocol for messages, and request message type (e.g., RPC (remote procedural call)/document). The MessageContext object's structure includes a request message and/or response message. Each message has a SOAPPart object and an optional Attachments object (they are Axis APIs). An FOP Web service includes the Attachments object.

The handlers are invoked in order, which is primarily determined by deployment configuration (server-config.wsdd). I will discuss Axis deployment configuration later. A MessageContext object passes through a configured set of handlers; each handler does whatever the MessageContext object requires. An FOP Web service includes a Handler(AttachmentHandler) that processes attachments included in the incoming request message and a Handler(SOAPFaultHandler) that processes SOAP Faults included in the response message. Figure 3 illustrates the Axis architecture.

Figure 3. Axis architecture and SOAP message processing. Click on thumbnail to view full-size image.

Microsoft .Net WSE architecture overview

FOP Web services implementation supports .Net clients by using Microsoft's WSE. WSE represents a subset of Microsoft's GXA (Global XML Architecture) and extends to standard SOAP functionalities. WSE includes:

  • WS-Security: Implements security requirements for Web services.
  • WS-Routing: SOAP-based protocol that routes SOAP packets from one point to another over different types of transport protocols, such as HTTP, TCP, asynchronous, etc.
  • WS-Referral: The WS-Referral protocol provides instructions about how you should route SOAP messages.
  • WS-Attachment: Lets you attach files, such as images, XML, etc., to a SOAP message without serializing its contents into XML. Examples include sending files such as images, XML, etc. At present, .Net implementation supports DIME attachments only, which creates interoperability issues between Java and .Net platforms. WSE uses DIME for sending and receiving SOAP messages with additional attachments, like binary files and XML fragments. DIME encapsulates a SOAP message and its related attachments in a MIME-like way. As with SOAP, you send DIME messages using standard transport protocols such as HTTP, TCP, and UDP. In FOP Web services, you use DIME attachments to send XML and XSL or FO files to the FOPService class.
  • WSE and filters (standard and custom): WSE provides standard sets of classes (under the Microsoft.Web.Services namespace) to implement the above functionalities. WSE includes a standard set of filters and allows custom filters to provide additional functionalities, such as managing SOAP Faults or encrypting and decrypting SOAP request and response messages. Standard and custom filters intercept SOAP request and response messages. WSE's built-in outgoing and incoming standard filter sets provide functionality for diagnostic, security, timestamp, referral, and routing features.

Figure 4 illustrates two custom filters, SOAPFaultOutputFilter and SOAPFaultInputFilter. The SOAPFaultOutputFilter class intercepts and processes SOAP Faults related to request messages and sends them to the Windows Event Log facility. Similarly, SOAPFaultInputFilter processes SOAP Faults related to response messages and sends them to the Windows Event Log facility. The custom filters derive from the SOAPOutputFilter (for request messages) and the SOAPInputFilter (for response messages) classes. You must add these filters to the PipeLine (Microsoft.Web.Services.PipeLine) class and must override ProcessMessage() methods of the SOAPOutputFilter and SOAPInputFilter classes. I'll show you code to implement custom filters later.

Figure 4. Microsoft's .Net WSE and filters (standard and custom). Click on thumbnail to view full-size image.

Java FOP Web services components

This section details FOP Web services' design and code. I start with high-level component diagrams and descriptions and then drill down to lower level class diagrams and descriptions. Later in the article, I cover how .Net clients invoke FOP Web services and handle response messages.

Table 1 shows FOP Web services' server-side components.

Table 1. FOP Web services' components and functionalities. Click on thumbnail to view full-size table.

Figure 5 displays the component diagram.

Figure 5. FOP Web services component diagram

Client component classes

Table 2 and Figure 6 show the client component and related classes.

Table 2. Client component classes and functionalities. Click on thumbnail to view full-size table.
Figure 6. Client component class diagram. Click on thumbnail to view full-size image.

Service component, subcomponents, and classes

Table 3 shows the service components, subcomponents, classes, and functionalities.

Table 3. Service component, subcomponents, classes, and functionalities. Click on thumbnail to view full-size table.

Figure 7 provides a component diagram.

Figure 7. Service component diagram

util component and classes

Table 4 shows the util component, and Figure 8 displays the util component class diagram.

Table 4. util component, classes, and functionalities. Click on thumbnail to view full-size table.
Figure 8. util component class diagram. Click on thumbnail to view full-size image.

Handler component and classes

Table 5 lists handler components.

Table 5. Handler component classes and functionalities. Click on thumbnail to view full-size table.

Figure 9 illustrates handler component classes.

Figure 9. Handler component class diagram

AttachmentHandler

Axis provides provisions to include custom handlers for plug-in custom functionality and make service implementation code scalable and reusable. For FOP Web services, you can implement the custom handler AttachmentHandler. The AttachmentHandler intercepts a SOAP request message from the MessageContext object. AttachmentHandler is a reusable component.

AttachmentHandler sample code

The following code sets properties in the MessageContext object. The FOPService class then accesses these properties (Axis AttachmentPart, filenames related to attachments, and number of attachments) to perform FOP transformation:

AttachmentPart attachments[] = getMessageAttachments(messageContext);
messageContext.setProperty("Attachments",attachments);
Message requestMessage = messageContext.getRequestMessage()
int attachmentCount = requestMessage.countAttachments();
String[] fileNames = getFileNamesofAttachments(attachments,attachmentCount);
messageContext.setProperty("File Names",fileNames);
String cnt = java.lang.String.valueOf(attachmentCount);
messageContext.setProperty("AttachmentCount",cnt);

FaultHandler

FOP Web services provide an Axis custom handler, the FaultHandler class, which intercepts a SOAP Fault (response) and logs it to a configurable log4j appender. This allows centralized and selective SOAP Fault logging, and is an extremely valuable feature for monitoring and maintaining Web services-based infrastructures. The FaultHandler is a reusable class.

FaultHandler sample code

The following FaultHandler code retrieves SOAP Fault elements (e.g., faultActor, faultString, etc.) from a message and sends them to the log4j appender:

final static Logger logger = Logger.getLogger(foo.com);
public void invoke(MessageContext messageContext) throws AxisFault {
    Message responseMessage = messageContext.getResponseMessage();
    SOAPEnvelope SOAPEnvelope =(SOAPEnvelope) responseMessage.getSOAPEnvelope();
    SOAPBodyElement respBody = SOAPEnvelope .getFirstBody();
    AxisFault af = ((SOAPFault) respBody).getFault();
    String faultString = af.getFaultString();
    String faultActor = af.getFaultActor();
    logger.error(faultString);
    logger.error(faultActor);
}

log4j property file

The following code lists the log4j property file:

log4j.rootCategory=ERROR, LOGFILE
# Set the enterprise logger category
# Rolling file appenders, with maximum size of 10 MB
# Maximum backup copies of log files are 5
log4j.logger.foo.com=DEBUG, LOGFILE
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.LOGFILE.File=fopwebservice.log
log4j.appender.LOGFILE.maxFileSize=10mb
log4j.appender.LOGFILE.maxBackupIndex=5
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%-5p %d{ISO8601} [%t] -
%m%n
log4j.appender.LOGFILE.Append=true

AttachmentHandler and FaultHandler configuration

This code illustrates the Axis server-config.wsdd file:

<service name="FOPService" provider="java:RPC">
  <requestFlow>
     <handler name="AttachmentHandler"  type="java:foo.com.AttachmentHandler">
     </handler>
  </requestFlow>
  <responseFlow>
     <handler name="FaultHandler"  type="java:foo.com.FaultHandler">
     </handler>
  </responseFlow>
</service>

Service component and classes

Table 6 shows the service component, and Figure 10 displays its class diagram.

Table 6. Service component, classes, and functionalities. Click on thumbnail to view full-size table.
Figure 10. Service component class diagram. Click on thumbnail to view full-size image.

The code listing below sets the FOP transformation result (Axis AtttachmentPart, object instance) in the Message object. The Axis engine then sends a response to the client. The logic calls the FOPService class's renderXMLXSL() method to perform transformation using Apache FOP APIs. This code implements the renderXMLXSL() method:

if (cnt == 2) {
    String xmlFileName = fileNames[0];
    String xslFileName = fileNames[1];
    File xmlFile = (new File(xmlFileName)).getAbsoluteFile();
    File xslFile = (new File(xslFileName)).getAbsoluteFile();
    // Perform FOP transformation
    DataHandler dataHandler = renderXMLXSL(format,xmlFile,xslFile);
    If (dataHandler != null) {
       Message responseMessage = messageContext.getResponseMessage();
       responseMessage.addAttachmentPart(new AttachmentPart(dataHandler));
    }
}

webserviceConfigurator component and classes

FOP Web services use XML to define their configuration parameters, such as end points, namespaces, and specific SOAP Faults. The naturally generic XML schema may include configuration parameters for multiple Web services. Each Web services configuration may include multiple SOAP Fault definitions. FOP Web services use the Jakarta Commons Digester to perform Java-XML bindings. The org.apache.commons.digester.Digester class provides Simple API for XML (SAX) parsing and rules-based processing of arbitrary XML documents. Given a set of rules, Digester can read and parse XML documents, instantiate Java objects (e.g., serialized objects), and call methods on the Java objects to populate them with data from the XML document. The following code lists part of the XML document:

<?xml version="1.0"?>
   <webservices version = "1.0">
        <webservice name = "FOPWebService">
             <config>
                 <endpoint>http://localhost:9080/ Service/services/FOPService</endpoint>
            </config>
            <SOAP-fault name="invalidnumberofattachments">
                <actor>FOPWebservice:FOPService:fopService</actor>
                <fault-code>SOAP-ENV:Client</fault-code>
                <fault-string>Numer of attachment(s) can be one or two only. Received three attachments</fault-string>
            </SOAP-fault>
       </webservice>
   </webservices>

The above code defines a SOAP Fault, invalidnumberofattachments. The definitions include actor, fault-code, and fault-string elements. The client sends three attachments, and consequently, the FOPService class sends a SOAP Fault to the client. The code also defines the FOP Web service's end point.

Table 7 lists the configurator component classes and functionalities. Figure 11 shows a class diagram of the configurator component, which processes the XML file.

Table 7. Configurator component classes and functionalities. Click on thumbnail to view full-size table.
Figure 11. Configurator component class diagram. Click on thumbnail to view full-size image.

Let's now examine how Digester performs Java-XML bindings.

First, you instantiate Digester and set its validation to false.

Digester digester = newDigester();
digester.push(this);
digester.setValidating(false);

The first set of rules for Digester creates WebServiceElement object instances on all webservices/webservice XML tags, populates the name property (WebServiceElement object) with values of the name attribute of all XML tags, and sends the WebServiceElement object instances to the addWebServiceElements() method:

// webservices/webservice elements
digester.addObjectCreate("webservices/webservice",
                         webServiceElementClass);
digester.addSetProperties("webservices/webservice","name","name");
digester.addSetNext("webservices/webservice",
                    "addWebServiceElements",
                    webServiceElementClass); 

The addWebServiceElements() method builds Java's Hashtable with an attribute value (e.g., name = "FOPWebService") as a key and object instance (WebServiceElement) as a value:

public void addWebServiceElements(WebServiceElement webServiceElement) {
        webServiceElementTable.put(webServiceElement.getName(),webServiceElement);
}   

The next set of rules relates to the XML file's webservices/webservice/config elements. The rules set creates ConfigElement object instances, populates them with the values of the XML document's webservices/webservice/config/endpoint elements, and sends the ConfigElement object instances to the WebServiceElement class's setConfigElement() method:

// webservices/webservice/config element
digester.addObjectCreate("webservices/webservice/config",
                         configElementClass);
digester.addBeanPropertySetter("webservices/webservice/config/endpoint",
                               "endPoint");
digester.addSetNext("webservices/webservice/config",
                     "setConfigElement",
                     configElementClass); 

The next set of rules relates to the XML document's webservices/webservice/SOAP-fault elements and sub-elements (e.g., actor, fault-code, and fault-string). The rules set creates SOAPFaultElement object instances and populates them with the values of the webservices/webservice/config/SOAP-fault element's sub-elements, and sends the SOAPFaultElement object instances to the WebServiceElement class's addSOAPFaultElement() method:

digester.addObjectCreate("webservices/webservice/SOAP-fault",
                          SOAPFaultElementClass);
digester.addSetProperties("webservices/webservice/SOAP-fault","name","name");
digester.addBeanPropertySetter("webservices/webservice/SOAP-fault/actor",
                               "actor");
digester.addBeanPropertySetter("webservices/webservice/SOAP-fault/fault-code",
                               "faultCode");
digester.addBeanPropertySetter("webservices/webservice/SOAP-fault/fault-string",
                               "faultString");
digester.addSetNext("webservices/webservice/SOAP-fault",
                    "addSOAPFaultElement",
                    SOAPFaultElementClass);

After defining rules, Digester's parse method parses the XML file. The XML file parameter passes the method:

digester.parse(new File(CONFIGFILE));

This is the code for the WebServiceElement class's setConfigElement() method:

public void setConfigElement(ConfigElement configElement) {
            this.configElement = configElement;
}

I show the code for the WebServiceElement class's addSOAPFaultElement() method below. The code sets Java's HashMap with its key value as the attribute value (e.g., name of SOAP-fault-element) and the SOAPFaultElement object instance:

public void addSOAPFaultElement(SOAPFaultElement SOAPFaultElement) {
this.SOAPFaultTable.put(SOAPFaultElement.getName(),SOAPFaultElement);
}

The following code illustrates logic you need to access objects instances, WebServiceElement, ConfigElement, and SOAPfaultElement:

//Instantiate WebServiceDigester class.
WebServiceDigester webServiceDigester = new WebServiceDigester();
//Invoke business rules, use Digester to parse XML file, and return HashMap of WebServiceElement.
HashMap webServiceElementHashMap = webServiceDigester.executeDigester();
//Retrieve WebServiceElement related to FOPWebService element in XML document.
WebServiceElement webServiceElement = (WebServiceElement) =
webServiceElementHashMap.get("FOPWebService");
//Retrieve ConfigElement related to FOPWebService element in XML document.
ConfigElement configElement =  webServiceElement.getConfigElement();
//Retrieve SOAPFaultElement related to FOPWebService element and specific SOAP-fault element in XML document.
HashMap SOAPFaultHashMap = webServiceElement.getSOAPFaultTable();
SOAPFaultElement SOAPFaultElement = (SOAPFaultElement)
SOAPFaultHashMap.get("invalidnumberofattachments");

You can now retrieve individual properties of the serialized object (e.g., ConfigElement and SOAPFaultElement).

FOP Web services unit testing

FOP Web services use JUnit to perform unit-level testing. Apache Axis's Website is an excellent resource to learn about Axis internals. It also includes JUnit test cases, which I strongly suggest studying.

From Java to .Net

I've discussed the design and code of Java FOP Web services, and explained Message, MessageContext, AxisFault, and handler objects. I also applied the Jakarta Commons Digester to configure Web services parameters. In the next section, I move on to the .Net client and its interface with FOP Web services.

.Net (C#) client components

This section discusses .Net class and sequence diagrams and provides code to implement key components of the .Net client environment to interface with FOP Web services.

.Net (C#) class and sequence diagrams

Table 8 summarizes .Net client classes and functionalities.

Table 8. .Net client classes and functionalities. Click on thumbnail to view full-size table.

Figure 12 and Figure 13 offer class and sequence diagrams of the .Net client implementation, respectively.

Figure 12. .Net (C#) client class diagram. Click on thumbnail to view full-size image.
Figure 13. .Net (C#) client sequence diagram. Click on thumbnail to view full-size image.

.Net client code review

The key issues for .Net client code implementation include:

  • Invoking the FOPService class and fopService() method with the .Net client's format parameter
  • Processing DIME attachments as part of the SOAP request message
  • Using WSE's custom filter to process SOAP Faults

Java FOP Web services invocation from a .Net client

The code below shows how a .Net client invokes the FOPService class and its method, fopService(). The code defines the FOP Web service's end point, sets the Path property to false (this relates to WSE-specific routing), and finally, invokes the FOP Web service:

public class FOPClient : Microsoft.Web.Services.WebServicesClientProtocol {
      //Constructor.
      public FOPClient() {
        this.Url = "http://localhost:8080/axis/services/FOPService";
      }
 
[System.Web.Services.Protocols.SOAPRpcMethodAttribute("",
           Action="http://localhost:8080/axis/services/FOPService/fopService",
           RequestNamespace="http://localhost:8080/axis/services/FOPService",
           ResponseNamespace="http://localhost:8080/axis/services/FOPService")]
    public void fopService(string format) {
           /// The Path property provides access to WS-Routing SOAP headers.
           /// Since Axis does not support WS-Routing; it should be set to
           ///false.
           /// 
           this.RequestSOAPContext.Path.MustUnderstand = false;
          /// Invokes an XML Web services method synchronously using
          /// SOAP. Method name of Web service is FOPService.
         /// An array of object[] is passed as parameter for the
         /// FOPService, which expects
         /// type of format for output (e.g., PDF).
        /// 
          this.Invoke("fopService", new object[] {format});
    }

Processing DIME attachments

The following code illustrates the inclusion of DIME attachments as part of a SOAP message. The attachments are XML and XSL files, which are input to the Java FOP Web service's FOPService class. As mentioned earlier, .Net only supports DIME attachments. Apache Axis supports DIME and MIME attachments:

Microsoft.Web.Services.Dime.DimeAttachment dimeAttachment;
dimeAttachment = 
      GetDimeAttachment("text/xml",@"c:\images\resume.xml",
                         TypeFormatEnum.MediaType);
this.RequestSOAPContext.Attachments.Add(dimeAttachment);
dimeAttachment = 
            GetDimeAttachment("text/xml",@"c:\images\resume.xsl",
                               TypeFormatEnum.MediaType);
this.RequestSOAPContext.Attachments.Add(dimeAttachment);

WSE custom filters process SOAP Faults

WSE places its standard set of filters and custom filters in a pipeline, represented by the Microsoft.Web.Services.Pipeline class. The custom filters derive from the classes Microsoft.Web.Services.SOAPOutputFilter (for request messages) and Microsoft.Web.Services.SOAPInputFilter (for response messages). You must add these filters to the PipeLine class, and they must override the ProcessMessage() method of the SOAPOutputFilter and SOAPInputFilter classes.

You can add custom filters to the PipeLine class as follows:

FOPClient client = new FOPClient();
client.Pipeline.OutputFilters.Add(new SOAPFaultOutputFilter());
client.Pipeline.InputFilters.Add(new SOAPFaultInputFilter());

The following code overrides the SOAPfaultInputFilter class's ProcessMessage() method. The SOAPEnvelope class, which represents the SOAP message (e.g., response message) passes the method. The code uses .Net's XPath implementation to process the incoming SOAP Fault message, displays the message to the user, and calls a method, WriteToEventLog(), to write it to the Windows Event Log:

public override void ProcessMessage(SoapEnvelope envelope) {
    XmlNamespaceManager nsMgr = new XmlNamespaceManager(envelope.NameTable);
    nsMgr.AddNamespace("soapenv","http://schemas.xmlsoap.org/soap/envelope/");
    XmlElement fault = (XmlElement) envelope.Body.SelectSingleNode("soapenv:Fault", nsMgr);
    // If there is a fault...
    if (fault != null)
    {
           // Get fault children
       XmlNode faultcode = fault.SelectSingleNode("faultcode");
       XmlNode faultstring = fault.SelectSingleNode("faultstring");
       XmlNode faultactor = fault.SelectSingleNode("faultactor");
       XmlNode detail = fault.SelectSingleNode("detail");
           // Format fault children into string
       StringBuilder faultcodeBuilder = new StringBuilder("fault code = " +
                                                         ((faultcode != null) ? faultcode.InnerText : "")); 
           StringBuilder faultstringBuilder = new StringBuilder("fault string = " +
       StringBuilder faultactorBuilder = new StringBuilder("fault actor = " +
       StringBuilder faultdetailBuilder = new StringBuilder("fault detail = " +
           string output = faultcodeBuilder.ToString() + "\n" +
                           faultstringBuilder.ToString() + "\n" +
                           faultactorBuilder.ToString() + "\n" +
                           faultdetailBuilder.ToString();
           MessageBox.Show(output,"SOAP fault - Input Filter",MessageBoxButtons.OK,MessageBoxIcon.Error);
           string msg = string.Format("{0}\n{1}\n{2}\n\n{3}",
                            ((faultcode != null) ? faultcode.InnerText : ""),
                    ((faultstring != null) ? faultstring.InnerText : ""),
                    ((faultactor != null) ? faultactor.InnerText : ""),
                    ((detail != null) ? detail.OuterXml : ""));
       // Write raw fault body to a byte array
           MemoryStream stm = new MemoryStream();
       StreamWriter sw = new StreamWriter(stm);
       sw.Write(fault.OuterXml);
       sw.Flush();
       byte[] buf = stm.ToArray();
           // Write fault data to event log  
       WriteToEventLog(output, "WSE", buf);
           sw.Close();
       }
}

Out of .Net

This concludes the .Net (C#) client implementations. I discussed how a .Net client constructs a SOAP message (with DIME attachments) and invokes an FOP Web service (Apache Axis Java implementation). I also looked at how a .Net client intercepts a SOAP Fault and logs it to the Windows Log Event system.

Wrap-up

This article covered:

  • A high-level FOP Web services overview
  • A high-level overview of Axis and .Net architectures
  • Custom handlers (Axis) and custom filters (.Net) used by FOP Web services
  • SOAP Fault monitoring and logging by clients and Web services
  • Browser-, Java-, and .Net-based clients and interfaces to FOP Web services
  • Design (e.g., class and sequence diagrams) and code walkthroughs for Java and .Net FOP Web services
  • Integration of Struts with FOP Web services
  • Axis deployment configuration
  • Usage of Java-XML bindings to configure FOP Web services

It is necessary to acquire knowledge of fundamental concepts for different types of technologies to successfully design and deploy Web services.

This article offered an in-depth discussion about exposing Apache's FOP implementation as a Web service using Apache's Axis implementation. You learned about interoperability issues between Java and .Net environments, specifically in the area of attachments. I demonstrated how you can apply custom filters and custom handlers to provide extensions to SOAP messages and make applications more scalable and reusable.

I sincerely thank David Tuttle, Shawn Seaton, and Richard Chennault of Kaiser Permanente for reviewing this article.

Avinash Gokli has extensive experience designing and developing software applications, such as expert systems, distributed applications, and healthcare systems based on PowerBuilder, Java 2 Platform, Enterprise Edition (J2EE), and XML/XSL. He has taught XML/XSLT and J2EE architecture at California State Polytechnic University, Pomona. He currently works for Kaiser Permanente in Pasadena, Calif., and designs Web services based on Java and .Net platforms and their related technologies.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more