The business object structure translates into Web Services using tools from Apache Axis. In the P.T. Monday Coffee Company application, all of the business object structures are collected, a topic covered in Chapter 7, "Exploring the Business Object Collection Pattern." Still, the Business Object pattern teaches you many things about the Web Service environment, so this will be a bit of a contrived sample, knowing that you will replace it with a more appropriate sample in the next chapter.
This example revolves around allowing customers to retrieve data about your company. You create a business object to represent your company and then expose the business object as a Web Service. This second step allows any type of client to access your company information.
The CompanyImpl class contains complex data in the form of an AddressImpl class, as well as simple primitive data, such as the name of the company. The classes adhere to the JavaBean semantics by surfacing get and set methods for primitive and complex data. The address implementation does not contain any additional complex data. Figure 6-3 shows the structure of the Java business objects.
In addition to the company business object, the conceptual Web Service diagram overlays the conventional Java business object implementation. For Web Services, CompanyImpl is the service implementation for a Web Service, which you simply call Company . Of interest is that the CompanyImpl signatures become the operations on the Web Service; the address class becomes complex data surfaced by the Company Web Service.
Different Web Service implementations expose the service implementation and the contained complex types in different ways, but for simple deployment, the business object interface becomes the Web Service interface. Apache Axis leverages the JavaBean semantics to determine the contents of the SOAP messages that move between a client and the Web Service. Axis has built-in serializers and deserializers that convert a JavaBean from the native Java class representation to the proper format for transfer via Simple Object Access Protocol (SOAP).
The next sections, "Deploying the Service Implementation" and "Using the Business Object," illustrate details of the deployment and usage of the Company Web Service that connects to the CompanyImpl service implementation. It is important to understand that not all Web Service implementations use the same techniques for exposing and consuming Web Services. On the other hand, all Web Service implementations must understand the SOAP messages (described briefly in "Understanding the SOAP Message Structure") and the Web Services Description Language (WSDL) files. The conversion from Java to the Web Services architecture illustrates many of the complexities of managing the architecture mismatch. The most complex details of the conversion between architectures is left to Apache Axis and other Web Service environments, as detailed in Chapter 4, "Exploring the Architecture Adapter Pattern."
From the Java classes detailed in Figure 6-3 that define the business object domain, you can directly deploy CompanyImpl as a Web Service. To do so in Apache Axis, you have to define the service as well as any associated complex types on which the service implementation relies. In this case, the CompanyImpl class forms the Web Service interface; any JavaBeans used by the Web Service become the additional complex types identified by a beanMapping tag. Listing 6-2 shows the Web Service Deployment Descriptor (WSDD) file used to deploy the CompanyImpl class to Apache Axis.
<service name="Company" provider="java:RPC"> <parameter name="className" value="com.servicefoundry.books.webservices.entities.CompanyImpl"/> <parameter name="scope" value="Application"/> <parameter name="allowedMethods" value="*"/> <beanMapping qname="myNS:CompanyAddress" xmlns:myNS="urn:Company" languageSpecificType= "java:com.servicefoundry.books.webservices.entities.AddressImpl"/> </service>
In Listing 6-2's beanMapping tag, notice the qualified name ( qname ) of CompanyAddress associated with the Company namespace. Also, notice the mapping you create to the AddressImpl class that implements the complex type. Using the WSDD file in Listing 6-2, you deploy the Web Service to a running Apache Axis instance.
After deployment, the WSDL file generated by Apache Axis exposes the address class as a complex type represented in the wsdl:types portion of the file. This portion of the file precedes specific operation definitions and can become extremely complex based on the nesting and richness of the object model within the service implementation. The Extensible Markup Language (XML) in Listing 6-3 identifies the CompanyAddress complex type and each of the elements that make up the CompanyAddress , including addressLine1 , city , state , and more.
<complexType name="CompanyAddress"> <sequence> <element name="addressLine2" nillable="true" type="xsd:string" /> <element name="addressLine1" nillable="true" type="xsd:string" /> <element name="city" nillable="true" type="xsd:string" /> <element name="state" nillable="true" type="xsd:string" /> <element name="zipCode" nillable="true" type="xsd:string" /> </sequence> </complexType>
Notice the correlation between the complexType name and the qualified name in the WSDD file. Also, notice that you did not explicitly identify the address interface and properties to Apache Axis; instead, Axis used reflection and the JavaBean patterns to determine what elements make up the CompanyAddress type.
This technique within Axis works for an extremely high percentage of JavaBean classes. There are also techniques for deploying special classes to aid in the serialization and deserialization of JavaBeans with special needs or classes that do not adhere to the JavaBean semantics, such as a java.util.Vector . I have chosen to avoid the complex objects and the objects unique to Java, which results in easier consumption by platforms other than Java. Unfortunately, you constrain yourself from leveraging all of the facilities of the Java platform for classes that become Web Services.
At this point, you have deployed an available business object with a Web Services interface. You can access it through constructing SOAP messages, binding to the deployed Company Web Service, and sending the SOAP messages. As discussed in earlier chapters, it is easier for the client to access the Web Service through architecture adapters generated by Apache Axis.
When you use Apache Axis to build Java adapters to SOAP using the WSDL2Java tool, you see a similar class hierarchy as the original that forms the service implementation. Be careful about getting used to the end-to-end Java with the Web Services as a remote proxy. It is critical you always remember that your Web Service does not require Java to access it. In fact, for a pure Java scenario, you would be far better off using Remote Method Invocation (RMI).
As previously mentioned, it is extremely important to note that instances of the CompanyAddress (the class representing the AddressImpl service-side class after a transformation through the Web Services environment) JavaBean reside only in the client's workspace. Changes to complex types using the mechanisms described in this chapter will only affect the address in the client's environment. To change the actual address implementation, you have to call the setAddress() method on the CompanyImpl class. This method call creates a SOAP message and deserializes the address into the proper XML representation that resides within the SOAP operation.
In Listing 6-4, the Java client-side code creates a representation of the Company Web Service for use by the Java program. Next, the code retrieves the address from the Web Service. This operation creates a local CompanyAddress instance with a copy of data from the server-side AddressImpl instance contained in the company. You can use this instance to see the contained data via the get methods on the CompanyAddress class.
CompanyImplService service = new CompanyImplServiceLocator(); CompanyImpl port = service.getCompany(); String name = port.getName(); System.out.println(name); CompanyAddress address = port.getAddress(); System.out.println(address.getAddressLine1() + " " + address.getAddressLine2()); System.out.println(address.getCity() + ", " + address.getState() + ", " + address.getZipCode());
If you want to change the address on the company, you would first change some data in a CompanyAddress instance, then call the setAddress method on the CompanyImpl architecture adapter. The line that retrieves the address, port.getAddress() , is a remote operation on the Company Web Service. The Web Service calls the appropriate getAddress() method on the service implementation, then takes the results and serializes the data into a SOAP document with the address instance of the CompanyAddress complex type. The client side decodes the SOAP information and turns it into the CompanyAddress object instance for use on the client.
Although the generated architecture adapters automate the entire communication path for us, it is still useful to look at the SOAP messages passed between the client and the Web Service. The SOAP message represents the address as nested XML response data, as shown in Listing 6-5.
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv=http://schemas.xmlsoap.org/soap/envelope/ xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <ns1:getAddressResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1= "http://localhost:8080/axis/services/Company/axis/services/Company"> <getAddressReturn href="#id0"/> </ns1:getAddressResponse> <multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:CompanyAddress" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:Company"> <state xsi:type="xsd:string">CO</state> <addressLine1 xsi:type="xsd:string">100</addressLine1> <city xsi:type="xsd:string">Broomfield</city> <zipCode xsi:type="xsd:string">80021</zipCode> <addressLine2 xsi:type="xsd:string">Eldorado Boulevard</addressLine2> </multiRef> </soapenv:Body> </soapenv:Envelope>
Tracing through the SOAP response, you will see correlations between the WSDD file, the XML schema representing the complex type from the WSDL file, and the client-side and server-side JavaBeans. The SOAP message originates as a set of JavaBeans within the service implementation. The Apache Axis engine converts the response into the SOAP message in Listing 6-5. The client-side architecture adapter, generated by Apache Axis, deserializes the SOAP message back into JavaBeans, similar to those within the service implementation. These conversion processes substantially increase the path length of a typical operation call; therefore, the performance of your operations that use Web Services are sure to suffer. Chapter 16, "Implementing the Data Transfer Object Pattern," and Chapter 17, "Exploring the Partial Population Pattern," introduce additional patterns for minimizing the number of requests to the Web Service.
The unfortunate side effect of moving toward Web Services is that your object design can suffer from the perspective of an object purist. You first stripped the complex logic from the object model (Chapter 8, "Exploring the Business Process Pattern" explains this detail in more depth), and now you are subtly flattening the object model itself. These are unfortunate side effects of dealing with an unknown set of client architectures and languages. The overall architecture of your system does not change as a result. The changes will be more subtle design and implementation changes in your business objects.
Interestingly, using Web Services is not the only influence on the structure of your business objects. Your choice of persistence mechanism will also lead you toward a flatter, more database-like object structure. Although Java Data Objects (JDO) professes to be a pure object-oriented and natural mechanism for persisting objects, it does influence the object structure.
There are alternatives to allowing the object design to suffer. Leaving complex logic on the object does not mean you cannot leverage Web Services or other platforms; it only means that some of the new features of Web Services, such as business process metadata languages, will be less accessible. Further, complex logic types and their translation to Web Services and subsequent client complexity can always be mitigated using custom deployment components , though this last strategy typically depends on the platform you use to deploy the Web Services. The reality of Web Services is that they do not excel in rich object orientation; after all, Web Services is not an object-oriented paradigm. Many developers experienced the same paradigm shift moving from Java to Enterprise JavaBeans (EJBs) and Container Managed Persistence (CMP). EJB is a component model with rich heritage in relational persistence models, especially in the CMP type of EJB. The influences are similar to reduce the complexity of the object orientation and become highly cohesive, loosely coupled components.
A final strategy to mitigate the paradigm shift is to allow your business objects to naturally reflect the business domain at design and implementation time. Then, build a set of classes, adhering to the Adapter pattern, to translate between the flat object representation and the richer representation.
Table 6-1 identifies the primary code discussed in this chapter, as well as related files of interest to you from the downloaded samples.
This is the CompanyImpl service implementation described in Figure 6-3. This class takes an interesting approach to constructing its address information; it goes to the P.T. Monday Coffee Company representation in UDDI to gather address information.
This is a client program that accesses the company Web Service.
Table 6-2 gives the targets to run for the ant environment to see the programs and chapter samples in operation. Before running any samples, be sure you read and perform all of the install steps in Appendix A.
Runs the client-side company access program