Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Build error-proof web services

Using the SOAP Fault mechanism to handle exceptions

Rachel Shen (rshen@ca.ibm.com), Software Developer, IBM Vancouver Innovation Centre
Rachel Shen is a software developer at the IBM Vancouver Innovation Centre. She graduated from the University of British Columbia with a master's degree in electrical and computing engineering. She has experience on e-business applications using the WebSphere family of tools, as well as XML and Java technologies. Recently, she has been working on Web services and P2P computing. She is also interested in software development best practices and extreme programming. You can contact her at rshen@ca.ibm.com.
Ernest Choi (ernestc@ca.ibm.com), Senior IT Specialist, IBM Vancouver Innovation Centre
Ernest Choi is a senior IT specialist at the IBM Vancouver Innovation Centre whose current responsibilities include leading project teams to design and implement middleware solutions using Java technology, WebSphere, and C++. After graduating from the University of British Columbia once in 1982 with a bachelor's degree in electrical engineering, he created application software with two startup firms while working towards his 1987 bachelor's degree in computer science (also from UBC). In 1989, he joined the IBM Toronto Lab to develop xlC, the first IBM C++ compiler for AIX. For personal reasons, Ernest moved to Vancouver in 1995 and has developed middleware servers ever since. He's implemented small servers from scratch using C++ and the Java platform as well as larger, more scalable servers using a variety of Web technologies such as EJBs, WebSphere, XML, SOAP, and WSDL. You can contact him at ernestc@ca.ibm.com.

Summary:  A well-designed system should always have a powerful mechanism for handling error conditions. Web service messages, commonly implemented as Simple Object Access Protocol (SOAP) messages over the Hypertext Transfer Protocol (HTTP), use SOAP's fault mechanism to encode server-side error conditions to the web service client. This article will introduce the SOAP fault mechanism and explain how version 2.2 of the Apache SOAP toolkit encodes server-side exceptions into a SOAP fault. This basic mechanism is then extended by creating fault listeners based on the Visitor design pattern to encode polymorphic exceptions into the SOAP fault. The article concludes by showing a WSDL binding that allows a SOAP client Java program to receive a SOAP fault as a Java exception object.

Date:  01 Aug 2002
Level:  Introductory

Activity:  8076 views
Comments:  

Web services have been well accepted by the industry to solve the problem of distributing processing across multiple platforms and systems. However, a well-designed software system should always have a powerful mechanism for handling error conditions. Web service clients need to recognize and to respond to errors on the server side, whether those errors are due to an errant service implementation, a bad client request, or normal operating difficulties. SOAP lets you communicate error information using the SOAP <Fault>. Since web service clients rely on WSDL to understand the format of request and response messages, WSDL also describes the fault details returned by the web services.

In this article, you'll learn what information can be put into a SOAP <Fault> element, and how to design a fault-handling system using version 2.2 of the Apache SOAP toolkit. On the client side, this article will also show you how to describe a SOAP <Fault> in WSDL.

The SOAP <Fault> element

Listing 1 is an example of a SOAP response message that contains a <Fault> element. The message has been transmitted via HTTP.


Listing 1. SOAP <Fault> message
                

HTTP/1.1 500 Internal Server Error
Server: WebSphere Application Server/4.0
Content-Type: text/xml; charset=utf-8
Set-Cookie: JSESSIONID=0000M1BYT2CGGVGAVUD3IOQG0EA:-1;Path=/
Cache-Control: no-cache="set-cookie,set-cookie2"
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Content-Length: 711
Content-Language: en
Connection: close

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Exception from service object: Errors occurred while processing the message.
</faultstring>
<faultactor>/FaultHandling/servlet/rpcrouter</faultactor>
<detail>
  <field>
    <name>ProductId</name>
    <error>ProductId does not exist.</error>
  </field>
  <field>
    <name>CurrencyCode</name>
    <error>CurrencyCode is not USD, CAD or CNY.</error>
  </field>
</detail>
</SOAP-ENV:Fault>

</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

From this example, you can see that if an error occurs at the web service's server, the HTTP 500 Internal Server Error is returned.

The SOAP <Fault> element is used to carry error and/or status information within a SOAP message. If present, the SOAP <Fault> element appears only once within a <Body> element. A <SOAP-ENV:Body> may not contain both a <SOAP-ENV:Fault> and a wrapper (that is, a returned value).

The <SOAP-ENV:Body> may contain a <SOAP-ENV:Fault>, which in turn must contain two elements: <faultcode> and <faultstring>. Two other elements, <faultactor> and <detail>, are optional. These elements serve the following functions:

  • <faultcode>: Provides an algorithmic mechanism for identifying the fault.
  • <faultstring>: Provides a human-readable explanation of the fault.
  • <faultactor>: Provides information about the actor in the message path that caused the fault to happen.
  • <detail>: Carries application-specific error information related to the <Body> element. All immediate child elements of the <detail> element are called detail entries, and each detail entry is encoded as an independent element within the <detail>.

Detail entries can be used to communicate customized fault messages. For example, in the SOAP fault message in Listing 1, the <detail> element has multiple <field> elements. And each <field> has <name> and <error> elements.

Fault handling in Axis

Axis, which is the next version of Apache SOAP, has just been released in a second beta version. Since Axis is still under construction, we will wait and see whether it is going to support WSDL <fault> elements in its Java2WSDL and WSDL2Java tools. Axis has changed the format of the deployment descriptor. However, by examining Axis's architecture guide, you can see that its mechanism for building SOAP fault message is much better than Apache SOAP's.


Fault handling using Apache SOAP 2.2

In order to demonstrate fault handling in web services, we have built for this article a web service called PriceChecker. All of our example code illustrates interaction between PriceChecker and a hypothetical client.

When an error occurs on the server side during PriceChecker's operation, the Apache SOAP server will attempt to capture an error state and then construct a SOAP <Fault> message containing a base set of information about the error.

However, it is useful to pass additional fault information in the SOAP <detail> section when an error state arises. Apache SOAP 2.2 provides a pluggable fault-handling mechanism into which one or more fault listeners may be registered to process faults.

There are two basic fault handlers in Apache SOAP:

  • org.apache.soap.server.DOMFaultListener
  • org.apache.soap.server.ExceptionFaultListener

DOMFaultListener adds a <DOM> element representing the root exception which occurred, while ExceptionFaultListener wraps the root exception in a parameter.

Fault handlers are registered by including one or more <faultListener> elements within the deployment descriptor of a service.

After the SOAP toolkit registers the DOMFaultListener, the deployment descriptor file will be similar to Listing 2.


Listing 2. Deployment descriptor registered with DOMFaultListener
                

<root>
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" 
id="http://tempuri.org/com.ibm.store.PriceChecker" checkMustUnderstands="false">
  <isd:provider type="java" scope="Application" methods="checkPrice">
    <isd:java class="com.ibm.store.PriceChecker" static="false"/>
  </isd:provider>
  <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>
</isd:service>
</root>

Listing 3 shows the web service's SOAP response to a malformed request using DOMFaultListener. This response wraps the stack trace of the server-side exception into the SOAP fault detail.


Listing 3. SOAP <Fault> message using DOMFaultListener
                

HTTP/1.1 500 Internal Server Error
Server: WebSphere Application Server/4.0
Content-Type: text/xml; charset=utf-8
Set-Cookie: JSESSIONID=0000JR51W32C2WEYLCKPDINFBIA:-1;Path=/
Cache-Control: no-cache="set-cookie,set-cookie2"
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Content-Length: 2936
Content-Language: en
Connection: close

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Exception from service object: ProductId doesn't exist.</faultstring>
<faultactor>/FaultHandling/servlet/rpcrouter</faultactor>
<detail>
<stackTrace>com.ibm.store.InvalidProductIdException: ProductId doesn't exist.
	at com.ibm.store.PriceChecker.checkPrice(PriceChecker.java:27)
	at java.lang.reflect.Method.invoke(Native Method)
	at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146)
	at org.apache.soap.providers.RPCJavaProvider.invoke(RPCJavaProvider.java:129)
	at org.apache.soap.server.http.RPCRouterServlet.doPost(RPCRouterServlet.java:287)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
	at com.ibm.servlet.engine.webapp.StrictServletInstance.doService
	(ServletManager.java:827)
	... ...
	at com.ibm.ws.http.HttpConnection.readAndHandleRequest(HttpConnection.java:313)
	at com.ibm.ws.http.HttpConnection.run(HttpConnection.java:242)
	at com.ibm.ws.util.CachedThread.run(ThreadPool.java:122)
</stackTrace>
</detail>
</SOAP-ENV:Fault>

</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Creating new fault handlers

If you want to create a new fault handler, you should create a Java class that implements the org.apache.soap.server.SOAPFaultListener interface. You need to implement the fault() method, which takes in a SOAPFaultEvent. The SOAPFaultEvent wraps the SOAP <Fault> and SOAP <Exception> objects that were created as a result of the error.

The class diagram of the fault-handling system in PriceChecker is shown in Figure 1.


Figure 1. Class diagram for fault handling in the PriceChecker web service
Class diagram for fault handling in the PriceChecker web serivce

On the server side, the web service throws InvalidInputException and InvalidServerDataException, which are inherited from InvalidDataException. InvalidDataFaultListener implements the fault() method to construct the SOAP <Fault> message.

The Visitor design pattern is used to separate the exception hierarchy from the code that builds the fault details. When the exception passed in this fault() method is an InvalidDataException (please refer to the InvalidDataFaultListener code in Figure 1), InvalidDataException::doTask(Task) is called. Because of Java polymorphism, it will call either InvalidInputException::doTask(Task) or InvalidServerDataException::doTask(Task) automatically. The code for doTask(Task) is shown in Listing 4.


Listing 4. InvalidInputException::doTask
                

public class InvalidInputException extends InvalidDataException{
	... ...
	public Vector doTask(Task task){
		return task.buildFaultDetails(this);
	}
}

You can see that, if an InvalidInputException is thrown, buildFaultDetails(InvalidInputException e) will be called eventually to create the SOAP fault message in Listing 1. If an InvalidServerDataException is thrown, buildFaultDetails(InvalidServerDataException e) will be called. Listings 5 and 6 show the code to build fault details for InvalidInputException and InvalidServerDataException, respectively.


Listing 5. Build fault details for InvalidInputException
                

public Vector buildFaultDetails(InvalidInputException e){
	Vector details = new Vector();
	DocumentBuilder xdb = XMLParserUtils.getXMLDocBuilder();
	Document doc = xdb.newDocument();
	
	String[] fieldNames = e.getFieldNames();
	String[] errMessages = e.getErrMessages();
	
	for (int i=0; i<fieldNames.length; i++){
		Element paElem = doc.createElement(FIELD);
		
		Element elem = doc.createElement(NAME);
		Text tn = doc.createTextNode(fieldNames[i]);
		elem.appendChild(tn);
		paElem.appendChild(elem);
		
		elem = doc.createElement(ERROR);
		tn = doc.createTextNode(errMessages[i]);
		elem.appendChild(tn);
		paElem.appendChild(elem);
		
		details.addElement(paElem);
	}        
      
	return details;
}


Listing 6. Build fault details for InvalidServerDataException
                

public Vector buildFaultDetails(InvalidServerDataException e){
	Vector details = new Vector();
	DocumentBuilder xdb = XMLParserUtils.getXMLDocBuilder();
	Document doc = xdb.newDocument();
	
	Element elem = doc.createElement(ERROR);
	Text tn = doc.createTextNode(e.getDetailMsg());
	elem.appendChild(tn);
	
	details.addElement(elem);
	
	return details;
}

Once you've created the fault handler, you'll need to register it in the deployment descriptor, as noted above. Listing 7 shows the new deployment descriptor.


Listing 7. Deployment descriptor registered with user-defined FaultListener
                

<root>
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" 
id="http://tempuri.org/com.ibm.store.PriceChecker" checkMustUnderstands="false">
  <isd:provider type="java" scope="Application" methods="checkPrice">
    <isd:java class="com.ibm.store.PriceChecker" static="false"/>
  </isd:provider>
  <isd:faultListener>com.ibm.store.InvalidDataFaultListener</isd:faultListener>
</isd:service>
</root>

When a SOAP request is sent to the web service and causes the server throw an InvalidInputException -- the SOAP request message in Listing 8 is an example of such a problematic message -- the SOAP response in Listing 1 is received on the client side. This response is built by the buildFaultDetails code in Listing 5.


Listing 8. SOAP request message that causes the server to throw an InvalidInputException
                

POST /FaultHandling/servlet/rpcrouter HTTP/1.0
Host: localhost:8080
Content-Type: text/xml; charset=utf-8
Content-Length: 547
SOAPAction: ""

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<ns1:checkPrice xmlns:ns1="http://tempuri.org/com.ibm.store.PriceChecker" 
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<productId xsi:type="xsd:string">012460</productId>
<currencyCode xsi:type="xsd:string">EUR</currencyCode>
</ns1:checkPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Similarly, when a SOAP request is sent to a web service and causes the server throw an InvalidServerDataException, the SOAP response in Listing 9 is received by the client. It is built by the buildFaultDetails code in Listing 6.


Listing 9. SOAP <Fault> message resulting from a server-side InvalidServerDataException
                

HTTP/1.1 500 Internal Server Error
Server: WebSphere Application Server/4.0
Content-Type: text/xml; charset=utf-8
Content-Length: 617
Content-Language: en
Connection: close

<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Exception from service object: Errors occurred while processing the message.
</faultstring>
<faultactor>/FaultHandling/servlet/rpcrouter</faultactor>
<detail>
<error>Exchange rate or unit price should be a number on the server.</error>
</detail>
</SOAP-ENV:Fault>

</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

WSDL for the SOAP <Fault> element

WSDL helps web services clients understand the format of SOAP request and response messages.

WSDL can describe the SOAP fault using different WSDL SOAP bindings. There are two commonly used SOAP bindings: document/literal and RPC/encoded. The major difference between the two is that the first contains a schema that fully describes the SOAP messages, while the second needs extra encoding rules to serialize the data into a message. (For more on document and RPC style, see Resources.)

Listing 10 shows the PriceChecker-binding.wsdl file. Most of the file was automatically generated by IBM's Web Services Toolkit (WSTK; see Resources for more information); the lines highlighted in red were added by hand afterwards to describe the SOAP fault for InvalidServerDataException. The description of InvalidInputDataException is not provided because it needs an extra XML schema section to describe the complexType in Listing 1.


Listing 10. PriceChecker-binding.wsdl
                

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="PriceCheckerRemoteInterface" 
targetNamespace="http://www.pricechecker.com/definitions/PriceCheckerRemoteInterface" 
xmlns="http://schemas.xmlsoap.org/wsdl/" 
xmlns:tns="http://www.pricechecker.com/definitions/PriceCheckerRemoteInterface" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
  <message name="checkPriceRequest">
    <part name="productId" type="xsd:string"/>
    <part name="currencyCode" type="xsd:string"/>
  </message>
  <message name="checkPriceResponse">
    <part name="result" type="xsd:double"/>
  </message>
  <message name="invalidServerDataFault">
    <part name="error" type="xsd:string"/>
  </message>
  <portType name="PriceCheckerJavaPortType">
    <operation name="checkPrice">
      <input name="checkPriceRequest" message="tns:checkPriceRequest"/>
      <output name="checkPriceResponse" message="tns:checkPriceResponse"/>
      <fault message="invalidServerDataFault" name="invalidServerDataFault"/>
    </operation>
  </portType>
  <binding name="PriceCheckerBinding" type="tns:PriceCheckerJavaPortType">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="checkPrice">
      <soap:operation soapAction="" style="rpc"/>
      <input name="checkPriceRequest">
        <soap:body use="encoded" 
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        namespace="http://tempuri.org/com.ibm.store.PriceChecker"/>
      </input>
      <output name="checkPriceResponse">
        <soap:body use="encoded" 
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        namespace="http://tempuri.org/com.ibm.store.PriceChecker"/>
      </output>
      <fault name="invalidServerDataFault">
        <soap:fault use="encoded" 
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        namespace="http://tempuri.org/com.ibm.store.PriceChecker"/>
      </fault>
    </operation>
  </binding>
</definitions>

You'll notice a message called invalidServerDataFault, which has one <part> called error. The checkPrice operation contains an <input>, an <output>, and a <fault> element. The corresponding <operation> element inside the <binding> also has a <fault> element. Within this <fault> element, you'll see the <soap:fault> element that defines it as using the encoded mode to serialize the error information.

The WSDL file in Listing 10 describes SOAP fault information. According to the file, you can write some client code to retrieve this information from the SOAP fault <detail> section.


Conclusions

After reading this article, you should have a grasp of fault handling in web services systems. The <detail> section of the SOAP fault message can be used to pass application-specific error information. The pluggable fault-handling mechanism in the Apache SOAP toolkit works on the event/listener model. You can use the basic fault handler provided by the Apache SOAP, or create your own. A better understanding of fault handling in web services should help you build more robust distributed applications and services.

The source code for the PriceChecker web service, which implements the fault handling discussed in this article, can be downloaded here. You can use the WSDL <fault> to describe the SOAP faults. Experiment for yourself to get a better feel for how these systems work.


Resources

About the authors

Rachel Shen is a software developer at the IBM Vancouver Innovation Centre. She graduated from the University of British Columbia with a master's degree in electrical and computing engineering. She has experience on e-business applications using the WebSphere family of tools, as well as XML and Java technologies. Recently, she has been working on Web services and P2P computing. She is also interested in software development best practices and extreme programming. You can contact her at rshen@ca.ibm.com.

Ernest Choi is a senior IT specialist at the IBM Vancouver Innovation Centre whose current responsibilities include leading project teams to design and implement middleware solutions using Java technology, WebSphere, and C++. After graduating from the University of British Columbia once in 1982 with a bachelor's degree in electrical engineering, he created application software with two startup firms while working towards his 1987 bachelor's degree in computer science (also from UBC). In 1989, he joined the IBM Toronto Lab to develop xlC, the first IBM C++ compiler for AIX. For personal reasons, Ernest moved to Vancouver in 1995 and has developed middleware servers ever since. He's implemented small servers from scratch using C++ and the Java platform as well as larger, more scalable servers using a variety of Web technologies such as EJBs, WebSphere, XML, SOAP, and WSDL. You can contact him at ernestc@ca.ibm.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=11695
ArticleTitle=Build error-proof web services
publish-date=08012002
author1-email=rshen@ca.ibm.com
author1-email-cc=
author2-email=ernestc@ca.ibm.com
author2-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers