Chapter 8. Integrating JSP and Web Services

CONTENTS

IN THIS CHAPTER

  •  Thinking in JSP and Web Services
  •  Integrating a Web Service into a JSP Page
  •  When Should You Build Your Own Web Service?
  •  Building a Corporate Web Service
  •  Apache SOAP Help
  •  Summary

In Chapter 3, "Understanding Web Services," we introduced the basic concepts of using a Web service. However, at that point of time, we merely looked at Web services, not how JSP and Web services can be used in harmony. Now it's time to go into high gear and begin integrating Web services into a JSP application.

This chapter is broken down into two major sections. The first section deals with how to integrate a Web service into a JSP project. This is a high-level discussion on when to consider using a Web service. More importantly, this section will review how to properly implement an external Web service call within a JSP page. The coding example of the first section is written in a relatively simple tone as an aid to newer JSP programmers. The goal is to show how to properly wrap a Web service into a JSP application.

The second section then details the actual design and building of a new Web service to work within a site. The example in the second section is geared toward an intermediate coding level. This section will

  • Review the design issues of using a Web service within a JSP application.

  • Take code from the previous chapter and wrap a Web service around it to demonstrate the techniques of building a Web service.

Thinking in JSP and Web Services

Web services shouldn't be thought of as a different outside toolset. Rather, Web services should be thought of as a new construct that JSP programmers have in their arsenal to implement. Over time, the concepts of using Web services will become as familiar as using tag libraries to a JSP programmer.

JSP programmers have six methods at their disposal with which to encapsulate their code:

  1. Scriptlet in the JSP page

  2. JavaBean object

  3. Tag library

  4. Web service

  5. Servlet

  6. Applet or client-side code

A JSP programmer can tackle any type of system with these six coding styles. From a programming point of view, a Web service is merely a distributed function the JSP programmer is accessing. Using a Web service is nothing new to a JSP programmer. It isn't much different than using a pre-made Java object. For us, this means that a Web service is implemented using a combination of JavaBeans (the actual service, or the call to access a remote service), servlets (our Apache SOAP service server), and plain old-fashioned Java programming (the actual code to use the service).

Back when I was taking advanced algebra, my college professor made the statement that even a student in third grade already knew most of the advanced techniques of abstract algebra. He then showed us, to our wonder, that abstract algebra wasn't anything new, but instead just a matter of how one thought about the math. This is the situation with Web services, in a sense. The concepts and tools are all the same; what's new is how to use the tools and integrate them into a project.

The question quickly becomes If using Web services is nothing new, then why use them at all? The answer is their capability to easily give the JSP programmer another location to distribute code processing. A Web service gives us the capability to move our programming logic out of our JSP application to another location. In addition, our Web service methods can be used easily by other applications outside of our JSP application.

A follow-up question to all this is When should I use a Web service? As with most things in JSP, the answer depends on many factors.

Using a Web service comes at a price. A Web service adds additional layers to a process call. These additional layers are usually outside of the current JSP application. The processing time for a Web service will typically be the greatest factor against using it, relative to the other programming styles available to a JSP programmer. From a practical viewpoint, programmers shouldn't build a Web service if a process is only geared towards a single project.

Keep in mind that most design practices tell programmers to always keep all business logic embedded within JavaBeans. This is so a single project can use tag libraries to expose the JavaBean for use within the project. However, if the need arises to create a Web service over time, it's easy to use the same JavaBeans to build a Web service. Case in point is our banner ad logic. In Chapter 7, "Successfully Using JSP and XML in an Application," a banner ad process was built to work for a single site. This chapter will expand the banner ad logic into a Web service. For this example, it's very easy to move the logic over and quickly build a Web service with minimal fuss. The key point is to keep any central business logic within JavaBeans or business objects.

Tag Libraries Versus Web Services

On a broader scale, you would use a Web service when thinking globally, and a tag library when thinking locally. Thinking locally means the logic will only be used internally within a JSP application. From a programming point of view, there exists an area of overlap where it would be practical to implement code as either a Web service or a tag library. It makes sense to use tag libraries to package code across many JSP Web applications when each instance of the tag library only produces local results relative to the project the tag resides within.

Tag libraries and Web services are vastly different constructs. However, there is some overlap in their functionalities. A tag library is a perfect tool with which to package code that will be used across many JSP projects. The ideal tag library is data- and project-independent. At their hearts, they're both processes, and it's in this sense that a Web service and tag library can be viewed as very similar entities.

Use a Web service if

  • The destination Web applications are built in languages other than JSP. This is one of the best reasons to use a Web service. It doesn't make a difference if the client using the service is an ASP project or a CGI project! Compare this to a tag library, which will only work within a JSP project.

  • You'd rather sell or package your product in a per-use model. The sales advantage of this is a higher profit margin and the prevention of pirated software from hurting sales. These are some of the reasons why Microsoft is switching to a service model in their software packages.

  • Your organization needs to ensure everyone is using the exact same version of the code. Since a Web service is centrally controlled, there exists only one version of the code. Version control is very easy to maintain with Web services.

Use a tag library if

  • You would rather sell or package your product as a component rather than on a per-use basis. Packaging software in component form is easier from a sales perspective, as currently it's a more commonly used model of business.

  • The logic is similar between projects, but in reality is application-dependent. Many times code or a process ends up being project-specific. Therefore, the code will be similar from project to project, but the final code needs to be modified slightly for each project. When this is the case, build an initial tag library as a starting point. Distribute the tag libraries and let each project modify the tag library to meet the project-specific needs. If you take this option, make sure each project renames and places a unique identifier on the project variations of the tag libraries. This will help prevent confusion between the similar tag libraries.

  • You want to keep the code as close to JSP as possible. Using a tag library keeps the code within the JSP project, without adding the additional overhead of learning the details of a Web service and the components required to make it all work.

  • Connectivity issues to your central Web service exist. Although this is the age of the Internet, countless variations of setup exist, along with many accessibility issues. It's conceivable that a set of Web services may not be accessible to a particular JSP project; perhaps the JSP project resides on an intranet, with no access to the services server. In this case, using a tag library to package the service functionality makes sense.

All things considered, there will be some overlap, and at times you will find it equally valid to place your code either as a Web service or as a tag library as a means to distribute the code/process across projects. At the times when it seems both options are in balance, choose the paradigm you are more comfortable with maintaining. My personal philosophy is this: When possible, keep the code within a tag library to stay as close to the JSP framework, for simplicity reasons.

Using Tag Libraries to Access a Web Service

Now let's introduce a slight chaos factor to the mix. Many times it will make sense to use both constructs at the same time. Web services are external function calls. Usually it's bad design practice to place a service call straight into a JSP page. Instead, from a practical design viewpoint, the service call should be wrapped within a JavaBean or a tag library. It makes sense, as the tag library will create an easy interface to use the Web service logic within the JSP page. In this case, the tag library isn't performing a process; rather, the tag library is just a simple interface to access the Web service.

While it's simple to access a Web service directly on a JSP page, doing so has the following problems:

  • Calling a Web service directly in a page creates a problem regarding how to cleanly handle problems. What happens if the service is unavailable? What happens if the service returns bad data? Because of these issues, a Web service call should be embedded within a JavaBean. It is possible to customize the JavaBean to handle any problems that might occur when calling the Web service.

  • A call to a more complicated Web service would quickly become very messy to do within a JSP page. Code within a JSP page is not object-oriented, and more complicated service calls should be wrapped within objects to make the logic easier to maintain and handle.

  • Ideally, a JSP page should be simple enough for Web designers to modify the page. Embedding a service call within a tag library gives all users a simple interface with which to access the Web service.

Now let's examine an example of how to cleanly implement a Web service within a JSP page.

Integrating a Web Service into a JSP Page

This section will expand the AccessService.jsp example shown in Listing 3.7 from Chapter 3. The AccessService.jsp page is a great example of why it isn't a good idea to call a Web service directly. The temperature service used in the example is merely a developer's example of a Web service. This service isn't going to always be up and running. From a development point of view, you should expect this service to be down as often as it is running. Also, developers should think of worst-case situations instead of perfect conditions. Programmers aren't considered saints if the application is running 98% of the time. The reality is that users will leave over time as the 2% failure rate slaps them in the face. Remember, it doesn't take much to get a tarnished reputation; it just takes one bad page to lose a customer. As an example, we will rebuild the AccessService.jsp page to handle the problem of an unavailable service.

Expanding the example will be a simple multiple-step process:

  1. Create a custom exception class for handling service-based errors.

  2. Create a JavaBean to encapsulate the Web service call.

  3. Create a tag library as a JSP interface for the service call.

The first step is to build our own exception class to handle any problems that might occur. The new class, shown in Listing 8.1, should be saved as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter8/ServiceBookException.java.

Listing 8.1 Custom Exception Class to Handle Service Calls
package xmlbook.chapter8; public class ServiceBookException extends Exception {     public ServiceBookException() {}     /* Create a few specialized constructors */     public ServiceBookException(int ai_error_level)     {   setErrorSeverity(ai_error_level);  }     public ServiceBookException(String as_note, String as_data, int ai_error)     {   super(as_note);         setDataRecieved(as_data);         setErrorSeverity(ai_error);     }     /* Store any data received from the service */     private String dataReceived = null;     public String getDataReceived(){return dataReceived;}     public void   setDataRecieved(String as_result)     {dataReceived = as_result;}     /* Store level of severity of the error */     private int    errorSeverity = 0;     public  int    getErrorSeverity(){return errorSeverity;}     public  void   setErrorSeverity(int ai_error) {errorSeverity = ai_error;}     /* Override the toString function to include our extra data */     public String toString()     { String ls_newMessage =  super.toString()   + "\n";       ls_newMessage       += "Error Level:"      + getErrorSeverity();       ls_newMessage       += "\n Received Data:" + getDataReceived();       return(ls_newMessage);     } }

The first thing to note is the customized Exception class. We have three options in how to approach handling both the data and the problems that occur when accessing the service. The first option is to build a special object to contain data being returned by the service. This object could also contain several parameters to store any error information that occurred during the process. The second option is to build a special Exception class to handle any problem encountered in using the Web service. The third option is to perform a combination of the first and second options.

We'll select the second option because the temperature service in this example currently only returns a simple String. If the logic accessed a service that returns a more complicated dataset, then it would be advisable to take the extra step to build a special Java class to contain the return data. I would recommend using a Java XML document representation of the incoming data.

The actual code is relatively straightforward. First, several overloaded constructors were created to make initialization of the class easy. Then several properties to describe an error were added to the class.

The new property of most consequence is the errorSeverity property:

private int    errorSeverity = 0;

This property will be used to help the system determine how to handle the error. We will use two rules here. First, any error with a negative severity needs to be logged with the JSP container. Second, any error that has a positive severity needs to be sent to the user.

The other major change is to override the toString function to include the additional error information stored in our error class:

 /* Override the toString function to include our extra data */  public String toString()  { String ls_newMessage =  super.toString()   + "\n";    ls_newMessage       += "Error Level:"      + getErrorSeverity();    ls_newMessage       += "\n Received Data:" + getDataReceived();    return(ls_newMessage); }

The advantage of building a customized exception class is that the service JavaBeans can easily pass additional information to our tag handler. With the extra information, the tag handler can react with more precision to problems with a service.

It's time to start the second step and build a JavaBean to access the Web service in question, as shown in Listing 8.2. The file to be created is webapps/xmlbook/WEB-INF/classes/xmlbook/chapter8/TempService.java.

Listing 8.2 Encapsulating the Temperature Service Call in a JavaBean
package xmlbook.chapter8; import java.net.*; import java.util.*; import org.apache.soap.*; import org.apache.soap.rpc.*; public class TempService extends Object { public TempService() {} public String getTemperature(String as_zip) throws ServiceBookException {   String ls_result  = "N/A";     /* Step 1) Validate the zip code to use.                If no zip code was provided then throw user error. */     if (as_zip == null || as_zip.length() == 0 || as_zip.length() > 5)     { throw new ServiceBookException("Require a valid Zip Code",as_zip,1);}     try     {     /* Step 2) Create the Call object to access the service with. */     Call call = new Call ();     call.setTargetObjectURI("urn:xmethods-Temperature");     call.setMethodName ("getTemp");     call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);     /* Step 3) Create the parameters to pass to the Web service. */     Vector params = new Vector ();     params.addElement (new Parameter("zipcode", String.class,as_zip, null));     call.setParams (params);     /*Step 4) Perform the call. */     URL url =     new URL ("http://services.xmethods.net:80/soap/servlet/rpcrouter");     Response resp = call.invoke (url, "");       /*Step 5) Determine what was returned, and sort out the data.*/       if (resp.generatedFault())       {Fault fault=resp.getFault();        ls_result = " Fault code: " + fault.getFaultCode();        ls_result = " Fault Description: " +fault.getFaultString();        throw  new ServiceBookException(ls_result,"Service is Unavailable",-1);       }       else       {Parameter result = resp.getReturnValue();        ls_result  = result.getValue().toString();       }     }     /* Step 6) Return any errors encountered along the way. */     catch(ServiceBookException e)     {/*If return had a service exception then throw it up the error chain.*/        throw e;     }     catch(Exception e)     {  //log any unknown error received.        throw new ServiceBookException(e.toString(),ls_result,-1);     }     /* Step 7) Once we have real data, perform basic validation                to check if it is reasonable. If not, return error. */     float lf_validate;     try     { lf_validate = Float.parseFloat(ls_result.trim());}     catch (NumberFormatException e)     { throw  new ServiceBookException("Bad Data Returned","N/A",0);}     if (lf_validate > 125 || lf_validate < -100)     { throw  new ServiceBookException("Bad Data Returned","N/A",0);}     /* Step 8) Whew! Return successful service call results. */     return ls_result; } }

After this is done, don't forget to compile the code.

The code is almost identical in logic (but not in appearance) to the code found in the AccessService.jsp example (Listing 3.7). The only difference is the addition of error handling to the logic. Because the service functionality is the same, we will only review the added error handling found here.

The first checking performed is to make sure the parameters being sent are valid. If a problem exists with the user-supplied data, the code creates an error. In this exception, a positive severity error will indicate that the user should be alerted about the current error:

if (as_zip == null || as_zip.length() == 0 || as_zip.length() > 5) { throw new ServiceBookException("Require a valid Zip Code",as_zip,1);}

The next error to look out for is when something goes wrong with the actual call to the service:

if (resp.generatedFault())       {Fault fault=resp.getFault();        ls_result = " Fault code: " + fault.getFaultCode();        ls_result = " Fault Description: " +fault.getFaultString();        throw  new ServiceBookException(ls_result,"Service is Unavailable",-1);       }

If something goes wrong here, the code creates a new ServiceBookException object. This time the code will flag the error to be logged within the JSP server's log file. The code then throws an error to be dealt with by the tag library.

Finally, if nothing went wrong with the actual service call, then the code validates the data received from the service call:

try     { li_validate = Integer.parseInt(ls_result.trim());}     catch (NumberFormatException e)     { throw  new ServiceBookException("Bad Data Returned","N/A",0);}     if (li_validate > 125 || li_validate < -100)     { throw  new ServiceBookException("Bad Data Returned","N/A",0);}

If the data is invalid, again the code reports the error. This time it indicates that it isn't sure what to do with the error, by logging the error with a severity of zero.

The JavaBean will never directly handle the error. All it should do is report any errors. The JavaBean makes suggestions on how to handle the error through the severity code within the ServiceBookException class. However, it's up to our application interface to actually take action upon an error. In this process, the interface to the application will be the tag handler being built in the next step. The tag handler will receive any error event data and then decide the appropriate actions to take when an error occurs. This makes the JavaBean purer, because it only has to handle its own business process of calling the Web service. It only knows how to deal with the Web service, pass data back to the application, and then pass back any errors that occurred.

When this JavaBean is used in production, the code should be re-factored down into several more functions. In this case, it's easier to teach the logic in a single function call. However, the logic can be broken down into several more generic service calls and groupings of logic. The goal should be to make the methods as atomic as possible. This makes maintenance simpler, and the code easier to reuse and document. The logic here has four actions: validate data coming in, set up the service call, perform the service call and validate the results. This functionality can be broken down into four smaller private functions to be accessed through a central public function.

It should be noted that the original JSP page did perform some error checking. All JSP pages have a try-catch block, but it's behind the scenes. When the JSP container builds a servlet, all code within a scriptlet is automatically placed within a try-catch block for us. The disadvantage of the JSP page creating the try-catch block for us is the lack of control once an error happens. In the code here, using our own try-catch blocks permits us to intercept and handle errors before the JSP page has a chance to report a generic error and confuse the users.

Now it's time to build the tag library to access the Web service JavaBean, as shown in Listing 8.3. The file should be saved as webapps/xmlbook/WEB-INF/classes/xmlbook/chapter8/TempServiceTag.java.

Listing 8.3 Building the Temperature Service Tag Handler
package xmlbook.chapter8; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; public class TempServiceTag extends TagSupport {   private String zipCode = null;     public void setZipCode(String as_zip) { zipCode = as_zip; }     public String getZipCode() { return zipCode; }     public int doEndTag() throws JspException     {   String ls_result  = "";         /* Step 1) Create our service object. */         TempService tempservice = new TempService();         /* Step 2) Access our service. Take corrective action on an error. */         try         { ls_result = tempservice.getTemperature(getZipCode()); }         catch(ServiceBookException sbe)         {   int li_error_severity = sbe.getErrorSeverity();             if (li_error_severity > 0)             { ls_result = sbe.getMessage();}             if (li_error_severity == 0)             { ls_result = sbe.getDataReceived();}             if (li_error_severity < 0)             { pageContext.getServletContext().log("Service Access Error",sbe);               ls_result = sbe.getDataReceived();             }         }         /* Step 3) Display the results. */         try         {   pageContext.getOut().print(ls_result);  }         catch(Exception e)         {   throw new JspException(e.toString());   }         return EVAL_PAGE;     } }

This tag handler has one attribute in which the JSP page will store the current zip code for us:

private String zipCode = null;     public void setZipCode(String as_zip) { zipCode = as_zip; }     public String getZipCode() { return zipCode; }

The tag handler will only begin processing when the end of the tag is encountered. This is because the code is placed within the doEndTag function that is triggered when the page reaches the end of our tag. Once called, the function will create a TempService object, pass in the zip code, and grab the result.

/* Step 2) Access our service. Take corrective action on an error. */         try         { ls_result = tempservice.getTemperature(getZipCode()); }         catch(ServiceBookException sbe)         {   int li_error_severity = sbe.getErrorSeverity();             if (li_error_severity > 0)             { ls_result = sbe.getMessage();}             if (li_error_severity == 0)             { ls_result = sbe.getDataReceived();}             if (li_error_severity < 0)             { pageContext.getServletContext().log("Service Access Error",sbe);               ls_result = sbe.getDataReceived();             }         }

However, in running the service request, the tag handler is also looking to see whether anything went wrong. When something does go wrong, the code takes different actions, depending on the severity of the error reported from the service JavaBean. For extremely serious errors (indicated with a negative severity number), the code will first access the application's implicit object through the pageContext.getServletContext() call. Once the handle is found, the code then writes the error straight to the JSP container's log file. For user-based errors, the code will display the error message generated by the service JavaBean. For various generic errors, it merely shows whatever result the service JavaBean decides is appropriate to return. In a normal service call, the response should be a little more robust on various error conditions.

This example merely shows the potential of what a JSP programmer can set up. It is always recommended to log more severe errors, such as a service call failure. This is especially true any time a programmer might be using an outside Web service. Keeping a physical log of the errors will be an important communication tools with any service provider.

Once the code receives the data, the next step is to send the results back to the JSP page. This is done by using the pageContext object to communicate with the calling JSP page. With the pageContext object, the code is able to get a handle to the out implicit object. With the out object, it's then possible to push the result into the output stream of the JSP page.

Also, if an exception occurs while trying to deal with the JSP page, the code will pass the error back up to the servlet to handle automatically:

throw new JspException(e.toString());

At this point, it would be better to push Exceptions back up to the JSP container to handle. An error here would be rare, and it would be better to have the JSP container just log a JSP tag-related error. We will handle the Web service errors and let the system handle the higher-level JSP errors.

Now that the tag handler is finished, the next step is to build the tag library descriptor (TLD) file, shown in Listing 8.4. The file being created is webapps/xmlbook/WEB-INF/services.tld.

Listing 8.4 Building the Services TLD File
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE taglib           PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"                  "http://java.sun.com/dtd/Web-jsptaglibrary_1_2.dtd"> <taglib>     <tlib-version>1.0</tlib-version>     <jsp-version>1.2</jsp-version>     <short-name>services</short-name>     <uri>xmlbook.services</uri>     <tag>         <name>gettemp</name>         <tag-class>xmlbook.chapter8.TempServiceTag</tag-class>         <body-content>empty</body-content>         <description>Accessing the Xmethods temperature service</description>         <attribute>             <name>zipCode</name>             <required>true</required>             <rtexprvalue>true</rtexprvalue>             <description>check temp at this zipcode</description>         </attribute>     </tag> </taglib>

The services.tld file is basic. Right now, only one tag reference exists for the current tag handler built for the temperature service call.

After the services.tld file is created, it is time to register the TLD in the web.xml file (webapps/xmlbook/WEB-INF/web.xml), as shown in Listing 8.5.

Listing 8.5 Modify the web.xml File to Register the Tag Library
<taglib>   <taglib-uri>xmlbook.services</taglib-uri>   <taglib-location>/WEB-INF/services.tld</taglib-location> </taglib>

Before running any code, stop and restart the Tomcat container; because web.xml was modified, Tomcat must register the new Java classes. Now it's time to use the Web service tag we just built. The JSP page in Listing 8.6 will be saved as webapps/xmlbook/chapter8/DailyTemperature.jsp.

Listing 8.6 Building a JSP Page to Access the Temperature Service
<%@page contentType="text/html"         import="xmlbook.chapter8.*"%> <%@taglib uri="xmlbook.services" prefix="service" %> <% String ls_zipcode = (String) request.getParameter("zip");    if (ls_zipcode== null) ls_zipcode = "07931"; %> <html> <head><title>Using Services in JSP</title></head> <body> The result of the web service call is <br/> The temperature at <%= ls_zipcode %> is currently: <service:gettemp zipCode="<%= ls_zipcode%>"/> <form  method="post" action="DailyTemperature.jsp">     <input type="text" name="zip" id="zip" value="<%= ls_zipcode %>" />     <input type="submit" value="Enter New Zip Code" /> </form> </body> </html>

Note that when using NetBeans, you might need to manually launch the JSP page from the browser. Why? NetBeans version 3.2.1 has problems finding the tag handler because of path issues. All the code is correct; it's just that NetBeans is unable to make the translation.

This page is much simpler than our original AccessService.jsp example. Of course, all the work is now happening within the JavaBean, and the tag handler is doing all of the processing to access that JavaBean. The biggest advantage is that when the Web service fails, instead of the JSP page exploding in a stack trace, the user receives cleaner results, as shown in Figure 8.1.

Figure 8.1. Two examples of calling a Web service with access errors.

graphics/08fig01.gif

It's clear from the clean presentation that having a failure reported in this fashion is the way to show problems to a user. More importantly, this code also tracks any serious errors in the Tomcat log file. When something goes wrong, the logs will give us a reliable record of what went wrong, as shown in Figure 8.2. Using log files to track problems will always be more reliable than relying on user feedback.

Figure 8.2. Showing the log file of the error produced from Figure 8.1.

graphics/08fig02.gif

The log files for Tomcat are stored under the tomcat/log directory.

The other major advantage of accessing the service call in this manner is the decoupling of the service call and the actual JSP page. If the code accessed services directly from the JSP page, any change in the service would mean changing each JSP page using that service. By moving to a tag library/JavaBean combination, we only need to maintain one set of objects compared to countless JSP pages. As an example, to change the error handling, just change the way the tag library reports the errors. If you want to change the error message, then only change the one service JavaBean. Maintenance and expansion become easy with this model.

A Tag Library/Service Warning

Using a tag library creates an easy-to-maintain interface within your JSP page. As a design rule, tag libraries should be used as often as possible when building reusable logic within a JSP page. However, consider another rule that is equally important: The performance of a JSP page must be acceptable for the user.

Tag libraries come with performance issues. Properly designed and used correctly, the average tag library produces acceptable response times. The trouble comes from the fact that "acceptable response time" is a relative term. Every project has its own definition of what is acceptable. However, no matter how you define what is acceptable for your project, tag libraries introduce an additional layer of logic to achieve reusability. Any tag library you build should be tested and optimized for performance. If a tag library cannot be built to be responsive enough, skip the tag library step and use JavaBeans and servlets. Use tag libraries with the understanding that your service calls won't always need a tag library interface.

Unfortunately, too many variables exist to give an acceptable practice list for using a tag library with Web services. Your JSP container, complexity of code, server load, Web service load, Web service reliability, and your project's response time needs will all play a factor in how well this combination will perform.

The main reason this is being strongly emphasized here is that the Web service call itself is an unpredictable factor in performance. This makes sense, considering that the Web service call is also going through many layers to get data. Using tag libraries and services together makes a lot of sense in terms of reusability. It's in the area of performance that the programmer needs to exercise extra caution. The main lesson is that testing is critical to ensuring acceptable performance. Don't let this warning scare you away from using tag libraries with Web services; let it merely reinforce the idea that our job as programmers includes performance testing.

Fixing Some Network Issues

Proxy servers and Web services don't always get along. A proxy server's tendency to act as a middleman can confuse a Web service. When a Web application is making a Web service call behind a proxy server, it might be required to modify the execution of the call in Listing 8.2 slightly. Use code similar to this:

SOAPHTTPConnection conn = new SOAPHTTPConnection(); conn.setProxyHost("your Web proxy name"); conn.setProxyPort(Port # Web proxy is listening on); Call call = new Call(); call.setSOAPTransport(conn);

This should permit the Apache SOAP server to navigate safely through a proxy server.

In addition, when the system has a firewall set up, other problems can be encountered by a Web service. In my setup, the code wouldn't work until the firewall's settings were modified as follows:

IPSec Pass through set to disabled.

IPSec (Internet Protocol Security) is a new security standard which happens to be secure enough to also block this Web service. The tale gets stranger: When the example code was executed, it would always work with the first request, but fail on the second request. Due to the success of the first, I initially assumed that the firewall wasn't the problem an incorrect assumption that ended up costing a full day of tracking down ghosts. The other interesting fact is that this code wouldn't work through an AT&T or MSN dial-up connection. This indicates that the AT&T and MSN dial-up provider has filter features that blocked the Web service request. The lesson learned is that entering the world of Web services also means crossing over at times to the networking world. This is an area where not all JSP programmers will have expertise with troubleshooting problems. For programmers not familiar with their network setup, try to make sure you have access to the network administrator to help solve firewall and proxy server problems.

Web Service Reliability

This topic was mentioned earlier, but because of the nature of Web services, warrants some extra discussion. The example shown here is only the first step in the proper handling of a service, wrapping the service call around objects and error handling. This should always be done as a matter of good programming practice. The second issue is the need to pick services built by outside sources carefully. A Web service user needs to make sure that any production service is both available and has acceptable response time 99.999% of the time. A service with only 99% reliability is not good enough 99% means 1 out of 100 customers will be lost. Over time, you'll lose more customers than is reasonable even with a 99% success rate.

To be practical, a Web service should only have a failure once every 10,000 or more uses. Is this unreasonable to expect? No, especially not when your business is on the line. Web services promise to help lower production costs by farming out various services; however, the cost savings are meaningless when you're losing users because of a poorly performing service. Balance this hidden cost when deciding to use an inexpensive Web service over a custom-built solution.

Ironically, a Web service should tend towards being a more reliable solution than a custom-built solution. Why? The Web service has a larger audience, and thus more time for the testing and removal of serious bugs thanks to the larger audience. Don't be the first person on the block to use a Web service. Let the market temper and prove a Web service before buying into it. Also, don't get trapped by a badly performing Web service. Understand the alternatives, and have backup plans in place if a Web service provider doesn't provide the level of service you expect. From a business angle, using a Web service is similar to using a host provider. Use the same care as when picking a hosting solution for a Web application when shopping for Web services.

When Should You Build Your Own Web Service?

Organizations using Web services have several angles from which to approach Web services. Up to this point, this chapter has concentrated on using Web services over building a Web service. Now it's time to look more closely at building your own Web services.

Let's examine some cases where a project should build its own Web service:

  • Your project wants to expose a method for other projects to use.

    With programming resources getting harder to maintain, being able to reuse your logic centrally has a high appeal for organizations that need to maintain many applications.

  • Your company has several applications that need to share a process or data.

    Many companies tend to collect computer systems like dandruff. In the past, few effective options were available to integrate different systems. Usually the answer was to rebuild a new system and integrate all the functionality of the older systems. This approach tends to be very expensive to implement or fail. The second approach is to share data, but sharing data tends to be a logistical nightmare, where data wars erupt over data ownership.

    Web services are a perfect solution for companies in this position. Web services are application-neutral, and can be built to centralize key processes and data handling between your multiple applications. Since XML is used to represent data, XSL transformations or programming logic can be used to transform data in line with your various applications.

  • To replace a poorly performing commercial service. Don't get stuck using a poorly performing Web service. The code is already set up to use a service, so it would be a simple (however, not necessarily cheap) solution to build a new module to replace a poor service provider. Services should be plug-and-play. By encapsulating them as shown earlier in this chapter, you can ensure that they truly are plug-and-play within all your JSP pages.

We will build two services in this book, the first of which will be in this chapter. It will take the banner text rotator from Chapter 7 and convert it to a Web service. This will be the fully integrated example, using XML, a database, and services promised earlier in the book. While very basic, the text rotator does have the practical use of being usable across multiple sites. As an example, if the JSP Insider Web site expanded to include various other Web sites, using a text banner service would make it easy to centralize the text messages for display at the top and bottom of each page across Web sites. As another use, this Web service can be modified to provide common HTML as output. This means that a Web service could replace include files or other static HTML sources. A large government or corporate Web site might find this to be an attractive way to provide static headers or footers. Since it's a Web service, any site using the service doesn't necessarily need to be on the same network. A Web service like this is a perfect method to promote centralized features, no matter where a Web site is located relative to the parent organization.

It's time to ponder these ideas a bit further. If a service similar to this one were to be built, it might seem to be a slow way to provide static content. The basic problem is bandwidth and round trips. Using a service to provide reusable static content would be an expensive solution in terms of service calls. It would be similar to the problem of using a JSP page to always re-parse static XML files. However, in this case, the solution is as simple as using smart caching techniques. To make the system faster with static content, the Web service calls could be cached to the application, session, or even static class variables within the initialization of the JSP page itself. Once cached, the data is then quickly resolved by a given JSP page.

JSP Pages Versus Web Service

One of the most interesting questions I have encountered to date is Why should a programmer use a Web service, when it's possible to build a JSP page to produce output for someone to use? In other words, a JSP page is doing the same thing as a Web service returning character-based data. Why use a Web service?

This turns out to be a hard question to answer. In a sense, accessing a JSP page could be considered the same as accessing a Web service. If you think about it, calling a JSP page is a large method packaged in the form of a servlet, which sends back character output. The biggest difference is in the packaging.

A Web service defines an interface through which it's possible to define exactly the data being sent to and from the Web service. The JSP page, on the other hand, just hands back a large character text stream in the HTTP wrapper. This makes it hard to parse a JSP result for finer work. This makes a Web service easier to use programmatically over a straight JSP page. Whenever fine control of the data being sent over a HTTP request is required, it is time to use a Web service.

The next fact to consider is state. One thing JSP and servlets give the programmer is a concept of state: application, session, page, and so on. A service isn't as tied to the concept of state. In fact, keeping a Web service stateless has the advantage of making a Web service easier to scale and move around from project to project. However, a Web service doesn't necessarily have access to maintaining state in the same manner as a JSP page. State within a Web service will depend on the Web service server implementation being used by a project. Some Web service servers will not maintain state; others, such as Microsoft's .NET, will provide their Web services the exact same options of state that an ASP page enjoys (application, session, and so on). Apache SOAP does provide state management, and we will explore this idea further.

Building a Corporate Web Service

A corporate Web service's implementation is the same as any other Web service. The difference is merely in usage. Rather than broadcasting the service for outside projects to use, the Web service will only be used by internal corporate projects. It should be mentioned that a corporate Web service might not be intranet-based, but rather based over the Internet. This would make sense when a company has many sites across the Internet, but not necessarily on the same internal network. An internal service could still be exposed on the Internet in order to allow your various sites access to the Web service. When this is the case, some additional steps of security should be taken to prevent other entities from accessing your Web service. In Chapter 15, "Advanced Application Design," we will examine several methods, such as servlet filtering and Tomcat security realms, to achieve some protection for your Web services.

Chapter 7 built the text banner rotator to use an XML model. Now let's expand the example to produce an internal Web service. The bulk of the code is going to stay the same. The new code will create and access the service. As a warning, be extra careful where the code is saved. This example places code into two different Web applications. The majority of the code will be saved to the SOAP Web application, which was set up in Chapter 3.

Goal of This Web Service Example

This example has several goals. The first and most basic goal is to show an integrated example of building and using a full Web service. The second goal is to show several more advanced concepts, such as how to use application and session state within a Web service.

Realities of Building a Web Service

In building Web services, I have discovered one abundantly clear truth: The way to build a Web service is heavily dependent on a Web service server. This has several distinct effects.

The first is that code written for one server might not work within another Web service server. For example, the SOAP server is not part of the J2EE specification. Code written for the Apache SOAP server will not port over to a custom Java SOAP-based server that comes bundled with a J2EE server (unless that J2EE server uses Apache SOAP). Of course, you could always install and use Apache SOAP within any J2EE implementation. However, the code is specific to the Web service implementation. The Web service calls are, in theory, platform- and language-independent; the final Web service code is extremely platform- and language-dependent. For your project, this means choosing a Web service server that meets long-term plans and needs. We should also confine the occurrence of any business logic to outside the actual Web service objects. This is to permit easier movement to a different Web service server when required to change platforms.

The second effect is one of learning. Lessons learned from one Web service server won't necessarily transfer to another. Automatic features of one service server will not be present in another server solution. The code in our example is based on the Apache SOAP server, and lessons learned here wouldn't be the same with the Microsoft SOAP server.

The last effect comes from the fact that Web service servers are based upon an implementation of SOAP and other standards. An implementation of any specification is more of a programmer's opinion on what the specification means. In practice, the various Web service servers are usually different in the usage and implementation of a Web service. This ends up creating problems for us as end users. This means that Web services cannot necessarily be accessed on all platforms in the exact same manner. Not all SOAP servers and clients can just talk to each other automatically. It's very similar to the old Java saying "Write once, test everywhere." The biggest area of heartache is between the Apache SOAP and Microsoft SOAP servers. A Web service written in one won't necessarily be accessible by the other. Although over time this problem is being resolved, earlier SOAP server/clients can be a pain to use for this reason. When writing a Web service to be accessed by a Web service client other than the one you are using, it would be wise to set up test cases for the various potential clients.

Setting Up the Example

The first step to setting up the example is to copy the contents of xmlbook/WEB-INF/classes/chapter7 over to soap/WEB-INF/classes/chapter7. We will be able to use this code directly from the Apache SOAP server.

The second step is to copy over the database initialization file. It is good practice to reuse as much code as possible. So, copy xmlbook/WEB-INF/database.xml over to soap/WEB-INF/database.xml.

Initializing Data

Following the pattern set up from earlier examples, this example will cache the static data someplace where the code can quickly access it. Normally, this would mean it's time to build a new listener file. Depending on the Web service server, this may or may not be an option. In our example, the Apache SOAP server runs within a servlet container to manage the Web service requests. It is still possible to build a listener object.

This time around, the banners will get stashed within an Array object stored in the application memory. The actual Web service will access this array of banners. Since the data doesn't change often, this example will only create it once upon starting up, and it will persist in memory.

NOTE

It's recommended to build an additional JSP page to perform the occasional update of the banner ad array as required by the system. This page is not built here, as it doesn't directly affect the overall Web service example for this chapter.

It's time to start coding. The first step is to initialize our banner data into the ServletContext. The code uses a ServletContextListener, so the data is created when the Apache Soap server begins running.

This listener file, as shown in Listing 8.7, will be saved to soap/WEB-INF/classes/chapter8/BannerInitialization.java.

Listing 8.7 Initializing the Data for the Banner Service
package xmlbook.chapter8; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import xmlbook.chapter7.*; public final class BannerInitialization implements ServletContextListener {    public void contextInitialized(ServletContextEvent event)      { ServletContext application = event.getServletContext();        try        {  String  ls_xml  = application.getRealPath("WEB-INF/database.xml");           DatabaseParameter dbdata = new DatabaseParameter(ls_xml);           BannerAdStore banners     = new BannerAdStore(dbdata);           application.setAttribute("Banners",banners.bannerArray ());        }        catch (Exception e)        { application.log("Problem encountered at startup:" + e.toString());}      }      public void contextDestroyed(ServletContextEvent event)      {  } }

The actual code here is nothing new. It uses our business objects from Chapter 7, and the logic for using the listener is almost identical to that shown in Chapter 7.

The next step is to register the listener file. This means updating the file soap/WEB-INF/web.xml, as shown in Listing 8.8. The additional lines of code are noted in bold.

Listing 8.8 Updating the web.xml File for the Banner Service
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE Web-app     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"     "http://java.sun.com/j2ee/dtds/Web-app_2_3.dtd"> <Web-app>   <display-name>Apache-SOAP</display-name>   <description>no description</description>   <listener>     <listener-class>         xmlbook.chapter8.BannerInitialization     </listener-class>   </listener>  etc., etc.,;file continues as normal.

In this example, please make sure to update web.xml to use 2.3 DTD.

Accessing Application Data

Everything looks as it should, but then when you start building a Web service, the realization strikes that you are building a Java class, not a JSP page or a tag handler. You might ask yourself: How do I access the ServletContext object?

This question will be complicated further when using a different SOAP server. Depending on how the class is created by the Web service server, the code might or might not have access to the ServletContext object. While it's reasonable to expect various Web service managers to give us access to this type of information, don't assume that that will always be the case. Two methods stand out in which to get the information from the Apache SOAP server.

The first method is a little strange. It's possible to use a combination of static variables and an implementation of the HttpSessionBindingListener class. By implementing HttpSessionBindingListener, our service class could be notified whenever the application variables are created or modified within the JSP container. By placing the data into static variables, the data is available for all instances of the service being called by users. I don't recommend using this method it's pointed out as an interesting and extreme means of getting to the data.

The second method is specific to the Apache SOAP server. It is possible to write the service methods to request a SOAPContext object. The Apache SOAP server will automatically pass this object to the method. Once this object is received, it only takes a little work and code to dig your way back to the ServletContext object of the Web application. This object will also get us back to the session and request information relative to the service call. The problem of course is that this method is specific to the Apache SOAP server.

Building the Actual Web Service

The next step is to build the class file that will be our Web service. This means creating the file soap/WEB-INF/classes/xmlbook/chapter8/ProduceBanner.java, as shown in Listing 8.9.

Listing 8.9 Creating a Banner Service
package xmlbook.chapter8; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpSession; import javax.servlet.ServletContext; import org.apache.soap.rpc.SOAPContext; import org.apache.soap.Constants; public class ProduceBanner implements java.io.Serializable {   private String[] banners  = null;     private int bannercount   = 0;     public ProduceBanner() {}     private void gatherBanners(SOAPContext a_soapcontext)     {   if (banners == null)         {   HttpServlet servlet = (HttpServlet)                         a_soapcontext.getProperty(Constants.BAG_HTTPSERVLET );             ServletContext application = servlet.getServletContext();             banners     = (String[]) application.getAttribute("Banners");             bannercount = banners.length;         }     }     private String randomChoice ()     {   int li_row = (int) (Math.random() * bannercount);         if (bannercount == 0)             return "Banner Initialization Failed. No Banners Stored in System";         return banners[li_row];     }     public String getBanner (SOAPContext a_soapcontext, boolean ab_order)     {   gatherBanners(a_soapcontext);         if (bannercount == 0)             return "Banner Initialization Failed. No Banners Stored in System";         if (ab_order)         {   HttpSession session = (HttpSession)                         a_soapcontext.getProperty(Constants.BAG_HTTPSESSION);             Integer lastbanner = (Integer) session.getAttribute("LastBanner");             int currentbanner  = 0;             if (lastbanner != null)             { currentbanner = lastbanner.intValue() + 1;}             if ( currentbanner + 1 > bannercount) currentbanner = 0;             String ls_return = "Banner Number: " + currentbanner + "<br>";             session.setAttribute("LastBanner",new Integer(currentbanner));             return (ls_return + banners[currentbanner]);         }         else         { return randomChoice();}     }     public String randomBanner (SOAPContext a_soapcontext)     {  String ls_return = "";        gatherBanners(a_soapcontext);        return randomChoice();     } }

Since this object will live as long as a user has a session, the code will stash the banners into its own array called banners to reduce the trips to the application object, as shown in the following code fragment. Although not technically necessary, it's done to illustrate that the Web service can keep data alive across several requests.

private String[] banners  = null; private int bannercount   = 0;

The gatherBanners method is used to populate these variables. This method also illustrates how to mine the application context. The trick is to get the current SOAPContext of the Web service. Once we have this object, it's possible to get a handle to the HttpServlet object that the Web service is embedded within:

HttpServlet servlet = (HttpServlet)                        a_soapcontext.getProperty(Constants.BAG_HTTPSERVLET );

Once we have the HttpServlet, it becomes a simple effort to call the getServletContext method that gives us access to the ServletContext, better known as the application object:

ServletContext application = servlet.getServletContext();

The whole trick is to get the SOAPContext object. To do this, define the Web service method with the extra argument of SOAPContext:

public String getBanner (SOAPContext a_soapcontext, boolean ab_order)

The Apache SOAP server will automatically pass this object to the method. Keep in mind that different SOAP servers most likely will take a different approach to this application context.

The other interesting fact within this code is that the getBanner method will track the user session by mining the SOAPContext object to get a handle to the session object:

HttpSession session = (HttpSession)                         a_soapcontext.getProperty(Constants.BAG_HTTPSESSION);

Once the session object is in hand, it's used in the same way as the JSP session object. This particular code example will only stash the current banner number the user is viewing. The code will increment the banner by one each time the user calls the Web page. While the code is simple, it's a practical example of how to access the session object.

The rest of the code is standard Java code to access and return the results of an array. The amount of code specific to the Web service is minor. The code in this example, which is Web service specific, is only code to access context information within which the Web service is running. Keeping this in mind, the majority of the code for most Web services will be business-specific, with only a few minor links back to the SOAP server. Most of the work to make the Web service happen occurs in the Web service server. It's the server's job to marshal data back and forth between the Web service and the client.

After compiling this Java class, the next step is to deploy it as a Web service on the server.

Deploying a Web Service

We have our service class, but the Web service server needs to be told how to access the class. This means the actual class in Listing 8.9 will be registered to the SOAP server. This registration will expose our service and its methods for access by a service call. In Chapter 3, Apache SOAP's graphical interface was introduced to show how to quickly register a service. This chapter will take a different approach. The problem with the graphical interface is re-entering everything to make a simple change (a reality when developing a Web service initially). Apache provides another method to use, create a deployment descriptor XML file, and then use the descriptor file to manually register the service with Apache SOAP. While this is a bit more up-front work for us, in the end it is easier than tweaking and experimenting with your Web service. Also, note that this method is specific to the Apache SOAP server. Different Web service servers will have other means of registering a Web service.

Let's first build the deployment descriptor XML file, as shown in Listing 8.10. The file to be created is xmlbook/chapter8/ProduceBannerService.xml.

Listing 8.10 Creating a Deployment Descriptor XML File
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"              id="xmlbook.bannerservice">   <isd:provider type="java"                 scope="Session"                 methods="getBanner randomBanner">     <isd:java class="xmlbook.chapter8.ProduceBanner" static="false"/>   </isd:provider> <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener> <isd:mappings>   <isd:map     encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"     xmlns:x=""     qname="x:order"     xml2JavaClassName="org.apache.soap.encoding.soapenc.BooleanDeserializer"/> </isd:mappings> </isd:service>

Notice that the file is being saved to the xmlbook application. It doesn't matter where we store this file. In fact, this example will create a simple JSP page to deploy the service from the xmlbook Web application.

Let's review the options and what they mean.

The isd:service element defines the actual reference identification for the Web service:

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"              id="xmlbook.bannerservice">

The most important data to notice here is the id attribute. The id is used to identify the actual service. When using a URI to reference the Web service in this example, it will be xmlbook.bannerservice.

The next element to examine is the provider element:

<isd:provider type="java"               scope="Session"               methods="getBanner randomBanner">   <isd:java class="xmlbook.chapter8.ProduceBanner" static="false"/> </isd:provider>

The provider defines the actual Web service, as described in Tables 8.1 and 8.2.

Table 8.1. Description of the isd:provider Element
Subelement Description
type

The provider language type. This can be java, script, or user-defined. Since our code is written in Java, the type will be java.

It's interesting to note that the possibility exists to write the service with JavaScript and embed the code within the deployment descriptor file.

scope

The life span of the Web service object.

Request: The service lasts only for the length of the actual service call.

Session: The object stays alive as long as the HTTP session is active for the service caller.

Application: Once the Web service object is created, every user shares it across all calls to the Web service.

methods A space-separated list of the methods the Web service exposes.

 

Table 8.2. Description of the isd:java Element
Subelement Description
class A fully qualified class name representing the actual Web service.
static True if the Web service methods are static.

Then the system is told how to handle errors with the isd:faultListener element:

<isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>

Apache SOAP supports two different fault listeners: org.apache.soap.server. DOMFaultListener and org.apache.soap.server.ExceptionFaultListener. The more commonly used is the DOMFaultListener. This listener will add a DOM element representing the actual exception to the SOAP message being sent back to the client. The ExceptionFaultListener adds an additional parameter to the SOAP message. Any exception is then returned within this extra parameter. This doesn't mean much to us as users, but it is telling Apache SOAP how to package up the errors. In all the examples, we are capturing the errors as if they have been packaged with the DOMFaultListener.

Now that the Web service is defined, the next step is to indicate what arguments are used within the Web service. This step is accomplished with the isd:mappings element. Each isd:map subelement will define one of our arguments:

<isd:mappings>   <isd:map     encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"     xmlns:x=""     qname="x:order"     xml2JavaClassName="org.apache.soap.encoding.soapenc.BooleanDeserializer"/> </isd:mappings>

Table 8.3 describes the element.

Table 8.3. Description of the isd:map Element
Subelement Description
encodingStyle The encoding style used within the SOAP message. In other words, this is the encoding rules used to define the data within the SOAP message. Usually this entry will be http://schemas.xmlsoap.org/soap/encoding/.
xmlns:x All XML elements being used as parameters need to be namespace qualified. However, in practice this can be left blank.
qname The name of the actual parameter. Notice in our example the xmlns:x namespace is appended to the name for us here.
xml2JavaClassName

This is the class used to translate the received XML parameter into a Java parameter. Apache SOAP will take care of marshaling the data to and from the method. Apache SOAP provides many pre-built classes to handle this conversion. For example, the BooleanDeserializer is used to convert a parameter to the Boolean wrapper class. If the parameter is a primitive Boolean, Apache SOAP will automatically convert the data to the primitive type. The Apache SOAP JavaDocs have a complete list of all the classes available for de-serialization of XML data.

Apache has classes for each of the Java primitive types, and standard objects such as Vector. For any custom class, a programmer will need to create his own conversion class.

java2XMLClassName Similar to the xml2JavaClassName parameter, except it refers to the class being used to serialize a result being sent from the Web service. This means the Java parameter is translated into the XML being sent in the SOAP message. The Apache SOAP JavaDocs have a complete list of all the classes available for serialization of Java to XML.
javaType Used to define your own custom JavaBean class for when a nonstandard class is being used as a parameter.

Where Is the WSDL?

With all this discussion about the deployment descriptor file, you might wonder why it isn't in the WSDL format? After all, WSDL is a specification used to define a Web service. WSDL is designed to provide a client with the information required to use a Web service. The Apache deployment descriptor is used to define a Web service for the server to register. These are two slightly different functions. The deployment descriptor contains some information not found in the WSDL format. Currently, there isn't any way to automatically convert from a WSDL file to a deployment descriptor or vice versa.

Because this example is only being used locally by us, we won't build a WSDL file. Chapter 14, " Building a Web Service," will get into building a WSDL file for the JSPBuzz Web service example.

Writing a JSP Page to Deploy the Descriptor File

All the examples that come with Apache SOAP use a .bat file to register the deployment descriptor file. Personally, as a JSP programmer, I prefer to build a JSP page to perform the functionality of the .bat file. So, we create a quick example page, as shown in Listing 8.11, named xmlbook/chapter8/DeployService.jsp.

Listing 8.11 Creating a Batch JSP Deployment Page
<%@ page import="java.net.URL,                  org.apache.soap.server.ServiceManagerClient" %> <% /* The location of our Apache SOAP Server */ URL url = new URL ("http://localhost:8080/soap/servlet/rpcrouter"); /* The location of our deployment file */ String  ls_xml  = application.getRealPath("chapter8/ProduceBannerService.xml"); /* Create a new service manager */ ServiceManagerClient service = new ServiceManagerClient(url); /* arguments are 1) Location of soap server                  2) The action to take                  3) The XML descriptor file */ String input[] = { "http://localhost:8080/soap/servlet/rpcrouter",                    "deploy",                    ls_xml} ; /* Deploy our service */ service.main(input); %> <html><head><title>Deploy Service</title></head><body> <h1>Deploy a Service</h1>     Finished Deploying The <%= ls_xml %> Service </body> </html>

Calling this page will deploy our service created in this example. Whenever you want to update the service, just update the XML descriptor file and resubmit this page. With this, I encourage you to change the parameters of the service around to see how doing so affects your results.

This page isn't fancy; it's just a simple page to deploy a Web service. However, in addition to being easier to use than a batch file, it also shows that the Web service can be managed remotely. If you haven't done so yet, execute this JSP page and move on to the next step of building the client side of accessing the Web service.

More on Security

Listing 8.11 wasn't programmed only to show how easy it is to manage Web services, but also to stress the importance of security. If your Web service server is available on the Internet, then extreme care should be taken to lock down unauthorized access. For example, it would be very simple to write a similar script to un-deploy a Web service on a Web service server. An outside programmer could even introduce undesired script-based Web services. A simple fix for Apache SOAP can be found at http://soap.manilasites.com/stories/storyReader$13. The point boils down to the fact that Web service servers are still new, and security holes are ever present. Extra time should be taken to evaluate the security of a Web service server. Chapter 15 will discuss this issue in greater detail.

Building a Page to Access the Service

The last step is to build a JSP page to access our new service. This part of the example will use the ServiceBookException class developed in the first half of the chapter to make life easier for us. Because we want to access the Web service from outside the Web service server, the code will be placed in the xmlbook Web application.

The first step is to create a generic JavaBean to access the Web service just deployed in the previous step. The JavaBean, shown in Listing 8.12, will be saved as xmlbook/WEB-INF/classes/xmlbook/chapter8/BannerService.java.

Listing 8.12 Creating a JavaBean to Access the Web Service
package xmlbook.chapter8; import java.net.URL; import java.util.Vector; import javax.servlet.http.HttpSession; import org.apache.soap.*; import org.apache.soap.rpc.*; public class BannerService extends Object { public BannerService() {} private String performCall(Call call) throws ServiceBookException     { String ls_result = "";       try       {  URL url = new URL ("http://localhost:8080/soap/servlet/rpcrouter");          Response resp = call.invoke (url, "");           if (resp.generatedFault())           {Fault fault=resp.getFault();            ls_result = " Fault code: " + fault.getFaultCode();            ls_result = " Fault Description: " +fault.getFaultString();            throw new ServiceBookException(ls_result,"Unavailable Service",-1);           }           else           {Parameter result = resp.getReturnValue();            ls_result  = result.getValue().toString();           }       }       catch(Exception e)       {throw new ServiceBookException(e.toString(),"Bad URL",0);}       /*Determine what was returned, and sort out the data.*/       return ls_result;     } public String getNextBanner(HttpSession session) throws ServiceBookException { String ls_result  = "";   Call call = (Call)session.getAttribute("CallBanner");   if (call == null)   {   call = new Call();       call.setTargetObjectURI("xmlbook.bannerservice");       call.setMethodName ("getBanner");       call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);       /* Create the parameters to pass to the Web service */       Vector params = new Vector ();       params.addElement (new Parameter("order", Boolean.class, "true", null));       call.setParams (params);       session.setAttribute("CallBanner",call);   }     try{ ls_result = performCall(call);}     catch(ServiceBookException sbe)     {ls_result = sbe.toString();}     return ls_result; } public String getRandomBanner() throws ServiceBookException     {   String ls_result  = "";         Call call = new Call();         call.setTargetObjectURI("xmlbook.bannerservice");         call.setMethodName ("randomBanner");         call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);         try{ ls_result = performCall(call);}         catch(ServiceBookException sbe)         {ls_result = sbe.toString();}         return ls_result;     } }

Let's examine what's happening in the JavaBean.

The method performCall is actually a repeat of previously used code to perform the service call. Because we have several methods that need to call the Web service, the code was re-factored out into a private reusable method.

The JavaBean then has two method calls. The simple getRandomBanner method is just a call to the Web service method named randomBanner. The actual call doesn't require any special processing.

The method that requires a bit more explanation is the getNextBanner method. The logic is very similar to previous methods we've built, except that the code needs to access the session object. In order for us to retain session state using the Apache SOAP server, the same Call object must be used every time.

The implications of this are twofold: Currently, to retain state using the Apache SOAP server means using the Apache SOAP client. Another SOAP client will not be able to retain session state across calls. The second implication is that to retain session within the Web service call, the Call object being used by the client must also be stashed away within the session object. The code checks to see if a Call object already exists. If the Call object exists, then it's retrieved and reused by the JavaBean. If it doesn't exist, a new Call object is created and stored away to the session object.

By default, the Apache SOAP server leaves session state on. To manually control the session state, it's possible to use the SOAPHTTPConnection object to control what is happening within the connection. The code will be similar to the following snippet:

/*  By default Apache Soap maintains state;     to do it manually these are     steps to turn it on or off.  */         SOAPHTTPConnection shc = new SOAPHTTPConnection ();         shc.setMaintainSession (true);         call.setSOAPTransport(shc);

The balance of the logic used within the JavaBean is similar to code used earlier in the chapter.

In this example, we will use the JavaBean straight within the JSP page. Technically, at this point the JSP page should wrap up the service call within a tag library. However, doing so here wouldn't show anything new. In addition, the current code is so simple that the only true reason to use the tag library would be to create some nicer error handling, as shown in the earlier examples of this chapter. As a result, this example will just directly call the JavaBean.

Save the JSP page shown in Listing 8.13 as xmlbook/xmlbook/chapter8/ShowBannerService.jsp.

Listing 8.13 Creating a JSP Page to Access the Web Service
<%@page contentType="text/html"         import="xmlbook.chapter8.*" %> <%    BannerService banner = new BannerService();  %> <html> <head><title>Running a Local Web Service</title></head> <body> The result of the getRandomBanner Web service call is <br/> <%= banner.getRandomBanner()%> <br/><br/> The result of the getNextBanner Web service call is <br/> <%= banner.getNextBanner(session) %> </body> </html>

When this page is executed, the output in Figure 8.3 is displayed.

Figure 8.3. Showing the result of calling our Web services.

graphics/08fig03.gif

Refresh the page several times to see that the service call is indeed retaining session state, and that the getNextBanner method is showing the banners in correct incremental order, rather than randomly.

Apache SOAP Help

This chapter only touched the surface of using Web services. Because the chapter used Apache SOAP as the SOAP server, a good portion of questions will be geared towards problems encountered with this server. The best resource to use is the archive of the Apache SOAP user community. The archive can be found at http://marc.theaimsgroup.com/?l=soap-user&r=1&w=2.

Unfortunately, the Apache SOAP server version 2.2 documentation isn't very good yet. The good news is that the Apache SOAP server is very widely used, and the archives of Apache SOAP are very good. They should be the first place to visit whenever you have a question.

Summary

This chapter reviewed the basics of using Web services within a JSP application. Building the Web service isn't hard, and much of the work is just encapsulating the logic to make the code easier to implement. The most difficult work isn't the code, but actually learning how to use the SOAP Web server software. Unfortunately, each server implementation will have its own unique learning curve. Web services are new, and the tools to implement a Web service tend to reflect this state with less-than-complete documentation. Expect to spend extra time sorting out the tools as they settle down into mature versions of production software.

The next chapter will cover more advanced XML topics, including additional Web service topics.

CONTENTS


JSP and XML[c] Integrating XML and Web Services in Your JSP Application
JSP and XML[c] Integrating XML and Web Services in Your JSP Application
ISBN: 672323540
EAN: N/A
Year: 2005
Pages: 26

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net