2010-04-14 - Jakarta Taglibs has been retired.

For more information, please explore the Attic.

Jakarta Project: IO Tag library: Tags for working with FTP, HTTP, HTTPS, XML-RPC and SOAP

Version: 1.0

Table of Contents

Overview

The IO library allows URLs and HTTP requests to be performed using JSP custom tags. This allows JSP to be used to perform HTTP GETs or PUT operations and to make XML-RPC and SOAP requests.

Currently the IO library supports any protocols supported by the Java URL class such as

For example if you wanted to include the README from the GNU FTP site in your JSP output you could do:-

<io:request url="ftp://ftp.gnu.org/README"/>

More likely is you'll want to include the result of a HTTP request. For example to include the home page of the Jakarta project in your JSP output you could use:-

<io:request url="http://jakarta.apache.org"/>

This single tag is surprisingly useful. <jsp:include> is only capable of including a servlet that is in your current web application. The <io:request> can be used to make 'server side include' style calls to any web server anywhere for any resource.

In the case of HTTP based calls, the default is to perform a HTTP GET. To make it easier to do other forms of HTTP requests such as POST and PUT there is another tag specialised for HTTP. The previous JSP could be replaced by the more verbose:-

<io:http url="http://jakarta.apache.org" action="GET"/>

To perform a HTTP POST we need to specify the data to be POSTED to the remote server.

<io:http url="something" action="POST">
  <io:body>
    data to be posted....
  </io:body>
</io:xmlrpc>

We may also wish to specify some query parameters as well as some HTTP headers.

<io:http url="something" action="POST">
  <io:param name="foo" value="123"/>
  <io:param name="bar">123</io:param>
  <io:header name="Content-Type" value="text/xml"/>
  <io:body>
    data to be posted....
  </io:body>
</io:xmlrpc>

Here we use a nested <io:body> tag to capture its body content for the data to be posted. The IO tags can be "piped" together like unix processes such that the data to be posted can be specified using an inner <io:request> tag. For example the following example reads a file "foo.txt" and posts it to the "bar" web service and displays the result in your JSP output:-

<io:http url="bar" action="POST">
  <io:request url="file://somewhere/foo.txt"/>
</io:xmlrpc>

The pipelining of tags will be discussed in more detail below. The new wave of Web Services are based on XML and usually use HTTP POST to send a request to a web service and a response is returned as XML. Calling web services with the IO tags is easy.

Here's an example of calling an XML-RPC web service using the <io:http> tag...

<io:http url="someXmlRpcUrl">
 <io:header name="Content-Type" value="text/xml"/>
 <io:body>
  <methodCall>
     <methodName>do.something</methodName>
     <params>
        <param>
           <value><i4>1234</i4></value>
           </param>
        </params>
     </methodCall>
 </io:body>
</io:http>

Notice here that we've added an <io:header> tag to set the content type of the posted data to be the XML mime type.

To make it easier when calling XML-RPC and SOAP based Web Services there are 2 new tags to wrap up calling XML-RPC and SOAP protocols. They allow more brief JSP code hiding some of the HTTP detail of the protocols. For example here's the same request again using <io:xmlrpc> tag instead:-

<io:xmlrpc url="someXmlRpcUrl">
 <io:body>
  <methodCall>
     <methodName>do.something</methodName>
     <params>
        <param>
           <value><i4>1234</i4></value>
           </param>
        </params>
     </methodCall>
 </io:body>
</io:xmlrpc>

And here is a SOAP request using the <io:soap> to request a fortune cookie from a SOAP web service on the net:-

<io:soap
    url="http://www.lemurlabs.com/rpcrouter"
    SOAPAction="urn:lemurlabs-Fortune">
 <io:body>
  <SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
      <m:getAnyFortune xmlns:m="urn:lemurlabs-Fortune"/>
    </SOAP-ENV:Body>
  </SOAP-ENV:Envelope>
 </io:body>
</io:soap>

Pipelining

You can nest JSP custom tags together so that the output of one tag serves as the input to another tag. For example:-

<foo:a>
    <foo:b/>
</foo:a>

This is quite like the piping that is available on Unix platforms, the output of program "b" can be piped into the input of program "a" via the command line:-

b | a

To be able to process the output of <foo:b> the <foo:a> tag must be a BodyTag. A BodyContent object will be created and the content of the <foo:a> tag, including the output of the <foo:b> tag, will be written to the BodyContent object. This mechanism can cause unwanted double buffering. For example consider this pseudo JSP code:-

<foo:searchAndReplace from="Spain" to="London">
    <file:read filename="Spain.txt"/>
    <file:write filename="London.txt"/>
</foo:searchAndReplace>

In the above example, all of the Spain.txt file will be read into memory into the BodyContent before the <foo:searchAndReplace> tag begins to process the file. This is unnecessary double buffering and a bad use of streams. Even worse is, if the file is big, you could run out of memory in a heavily loaded server.

The IO tag library currently has a "tag pipelining" proposal integrated into it. This proposal is the work of from Pierre Delisle and James Strachan and is intended to avoid this unnecessary double buffering.

Tag Pipelining Proposal

The basic idea is that in the example below, the <file:read> tag should be able to pass a Reader object straight into the <foo:searchAndReplace> tag for it to use directly and thus avoiding unnecessary double buffering.

<foo:searchAndReplace from="foo" to="bar">
    <file:read file="foo.txt"/>
</foo:searchAndReplace>

Not only does this optimise away double buffering, but it allows any inner tag produce the Reader object used by the <foo:searchAndReplace> tag. This makes the <foo:searchAndReplace> really reusable as it can take any Reader, whether a local URI, a remote URL, a file, a socket, some text or whatever. It provides a clean separation of responsibility between "transformer" tags which process information and creators of Reader and Writer objects.

How this Reader and Writer passing mechanism should work and what the API should look like is still an active area of discussion. A brief overview of my current proposal now follows. Please check out taglibs-dev for the latest developments ;-)

A tag which can consume textual input should implement the following interface:-

    public interface PipeConsumer {
        public void setReader( Reader reader );
    }

A tag which can produce textual input should implement the following interface:-

    public interface PipeProducer {
        public void setWriter( Writer writer );
    }

These two interfaces allow "transformer" tags to be written such that their Reader and Writer objects can be configured in a flexible variety of ways. These "transformer" tags can also be pipelined together in an efficient manner similar to the pipelining of Unix processes.

Examples of transformer tags could be to perform text based search and replace, screen scraping, styling of XML or the calling some external web service (via HTTP, XML-RPC, SOAP or whatever) and returning the results.

A possible base class for a transformer tag could look something like the following:-

public abstract class TransformerTagSupport
        extends BodyTagSupport
        implements PipeConsumer, PipeProducer {

    private Reader reader;
    private Writer writer;

    // PipeConsumer interface
    public void setReader(reader) {
        this.reader = reader;
    }

    // PipeProducer interface
    public void setWriter(writer) {
        this.writer = writer;
    }

    // Tag interface
    public int doEndTag() throws JspException {
        if ( reader == null ) {
            // a child tag has not set my input source
            // so I'll read from my BodyContent
            reader = bodyContent.getReader();
        }
        if ( writer == null ) {
            // a child tag has not set my output destination
            // so I'll write to my enclosing writer
            writer = pageContent.getOut();
        }
        transform( reader, writer );
        return EVAL_PAGE;
    }

    // actually do the transformation,
    // reading from reader and writing to writer
    protected abstract void transform( 
        Reader reader, Writer writer 
    ) throws JspException;
}

A transformer tag could have its reader and/or writer configured via scriptlet expressions in JSP.

<foo:myTransformer reader="<%= new MyReader() %>" writer="<%= new MyWriter() %>"/>

Or the transformer tag could have an inner tag set its Reader or Writer. The inner tag could use the PipeConsumer or PipeProducer interfaces directly it could use Introspection to set the "reader" or "writer" properties. If no values are configured in the transformer tag then the natural JSP defaults are used, the Reader from the tags BodyContent is used with the Writer from the current PageContext.

So the following would work without any direct pipelining:-

<foo:searchAndReplace from="Spain" to="London">
    The rain in Spain
</foo:searchAndReplace>

Or inner tags could provide the Reader and Writer:-

<foo:searchAndReplace from="Spain" to="London">
    <file:read filename="Spain.txt"/>
    <file:write filename="London.txt"/>
</foo:searchAndReplace>

This pipelining proposal has been implemented in the IO tag library so that the IO tags can be pipelined together efficiently and flexibly without unnecessary double buffering.

Here's an example of more intense pipelining,

<xsl:apply xsl="someStylesheet.xsl">
  <io:soap url="someSoapURL" SOAPAction="doSomething">
    <xsl:apply xsl="xmlRpcToSoap.xsl">
      <io:xmlrpc url="someXmlRpcUrl">
        <io:body>
          <methodCall>
            <methodName>do.something</methodName>
              <params>
                <param>
                  <value><i4>1234</i4></value>
                </param>
              </params>
          </methodCall>
        </io:body>
      </io:xmlrpc>

    </xsl:apply>
  </io:soap>
</io:xmlrpc>

The above example looks like a relatively small block of XML but its actually doing quite a lot. Reading from inside-out, its calling an XML-RPC service with some XML data then using XSLT to style the result into a SOAP request which is then HTTP POSTed into a SOAP service. The result of the SOAP request is then styled again using XSLT and output to the users browser.

Requirements

Configuration

Follow these steps to configure your web application with this tag library:

To use the tags from this library in your JSP pages, add the following directive at the top of each page:

<%@ taglib uri="http://jakarta.apache.org/taglibs/io-1.0" prefix="io" %>

where "io" is the tag name prefix you wish to use for tags from this library. You can change this value to any prefix you like.

Tag Summary

IO Tags
request Requests the content of the given URL.
http Performs a HTTP request on the given URL with the specified action and optional body. If no action is specified then it defaults to "GET".
header Defines a URL or HTTP header for the current <request> or <http> tag. The value of the header can be specified via the value attribute otherwise the value of the tags body is taken instead.
param Defines a query argument for the URL of the current request. The value of the parameter can be specified as an attribute otherwise the value of the tags body is taken instead.
soap Performs a HTTP SOAP request on the given URL, SOAPAction and body.
xmlrpc Performs an XML RPC request on the given URL.
body Contains the body of a HTTP POST, XML-RPC or SOAP request which is sent so the URL as input.
pipe Acts like a Unix pipe between tags that are not capable of piping themselves. A pipe can take some input or some output or both.
get Returns the bean or bean property and pipes it into its parent PipeConsumer tag.
 

Tag Reference

request Availability: 

Requests the content of the given URL.

Tag BodyJSP    
Restrictions

None

AttributesNameRequired Runtime Expression Evaluation Availability
 url  Yes   Yes  
 

The URL to request. If the URL starts with '/' then it is treated as a local http request

 encoding  No   Yes  
 

Sets the character encoding used for this request. If none is specified then the JVM's default is used.

 output  No   Yes  
 

If set to true the output is enabled otherwise the output from the request is ignored.

 input  No   Yes  
 

If set to true then input is enabled otherwise the input to the request is ignored.

VariablesNone
 
 


<io:url url="/WEB-INF/foo.xml"/>

http Availability: 

Performs a HTTP request on the given URL with the specified action and optional body. If no action is specified then it defaults to "GET".

Tag BodyJSP    
Restrictions

None

AttributesNameRequired Runtime Expression Evaluation Availability
 url  Yes   Yes  
 

The URL to request. If the URL starts with '/' then it is treated as a local http request

 action  No   Yes  
 

Defines the HTTP action, either GET, POST, PUT, HEAD or some extended HTTP action such as the WEB-DAV protocol.

 encoding  No   Yes  
 

Sets the character encoding used for this request. If none is specified then the JVM's default is used.

 output  No   Yes  
 

If set to true the output is enabled otherwise the output from the request is ignored.

 input  No   Yes  
 

If set to true then input is enabled otherwise the input to the request is ignored.

VariablesNone
 
 


<io:http url="http://jakarta.apache.org" action="GET"/>

header Availability: 

Defines a URL or HTTP header for the current <request> or <http> tag. The value of the header can be specified via the value attribute otherwise the value of the tags body is taken instead.

Tag BodyJSP    
Restrictions

None

AttributesNameRequired Runtime Expression Evaluation Availability
 name  Yes   Yes  
 

Defines the name of the header to set.

 value  No   Yes  
 

Defines the value of the header. If this attribute is not specified then the body of this tag is used instead.

VariablesNone
 
 


<io:http url="http://jakarta.apache.org" action="GET"/>
  <io:header name="Content-Type" value="text/xml"/>
</io:http>

<io:http url="http://jakarta.apache.org" action="GET"/>
  <io:header name="Content-Type">text/xml</io:header>
</io:http>

param Availability: 

Defines a query argument for the URL of the current request. The value of the parameter can be specified as an attribute otherwise the value of the tags body is taken instead.

Tag BodyJSP    
Restrictions

Must be specified before any <http:header> or <http:body> tags

AttributesNameRequired Runtime Expression Evaluation Availability
 name  Yes   Yes  
 

Defines the name of the URL query parameter.

 value  No   Yes  
 

Defines the value of the URL query parameter. If this attribute is not specified then the body of this tag is used instead.

VariablesNone
 
 


<io:http url="http://jakarta.apache.org" action="GET"/>
  <io:param name="foo" value="123"/>
  <io:param name="bar">123</io:param>
</io:http>

soap Availability: 

Performs a HTTP SOAP request on the given URL, SOAPAction and body.

Tag BodyJSP    
Restrictions

None

AttributesNameRequired Runtime Expression Evaluation Availability
 url  Yes   Yes  
 

The SOAP endpoint URL. If the URL starts with '/' then it is treated as a local http request

 SOAPAction  Yes   Yes  
 

The SOAP action to use

 encoding  No   Yes  
 

Sets the character encoding used for this request. If none is specified then the JVM's default is used.

VariablesNone
 
 


<io:soap 
    url="http://services.xmethods.net:80/perl/soaplite.cgi" 
    SOAPAction="urn:xmethodsBabelFish#BabelFish">
 <io:body>
  <SOAP-ENV:Envelope 
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
      <m:BabelFish xmlns:m="urn:xmethodsBabelFish">
        <translationmode>en_fr</translationmode>      
        <sourcedata>SOAP is quite easy with JSP.</sourcedata>    
      </m:BabelFish>
    </SOAP-ENV:Body>
  </SOAP-ENV:Envelope>
 </io:body>
</io:soap>

xmlrpc Availability: 

Performs an XML RPC request on the given URL.

Tag BodyJSP    
Restrictions

None

AttributesNameRequired Runtime Expression Evaluation Availability
 url  Yes   Yes  
 

The XML-RPC endpoint URL. If the URL starts with '/' then it is treated as a local http request

 userAgent  No   Yes  
 

The XML-RPC user agent

 encoding  No   Yes  
 

Sets the character encoding used for this request. If none is specified then the JVM's default is used.

VariablesNone
 
 


<io:xmlrpc url="/RPC2">
 <io:body>
  <?xml version="1.0"?>
  <methodCall>
     <methodName>examples.getStateName</methodName>
     <params>
        <param>
           <value><i4>41</i4></value>
           </param>
        </params>
     </methodCall>
 </io:body>
</io:xmlrpc>

body Availability: 

Contains the body of a HTTP POST, XML-RPC or SOAP request which is sent so the URL as input.

Tag BodyJSP    
Restrictions

AttributesNameRequired Runtime Expression Evaluation Availability
 reader  No   Yes  
 

The Reader used to find the body to be used. If this variable is not specified then the body of the current tag is used.

VariablesNone
 
 


<io:http url="http://www.acme.com/foo" action="POST">
  <io:body> this is the body to be posted </io:body>
</io:http>

<io:http url="http://www.acme.com/foo" action="POST">
  <io:body reader="<%= request.getReader() %>"/>
</io:http>

pipe Availability: 

Acts like a Unix pipe between tags that are not capable of piping themselves. A pipe can take some input or some output or both.

Tag BodyJSP    
Restrictions

AttributesNameRequired Runtime Expression Evaluation Availability
 reader  No   Yes  
 

The Reader used to find the input to be used. If this variable is not specified then the body of the current tag is used.

 writer  No   Yes  
 

The Writer to be used as the destination of the pipe. If this variable is not specified then the current output is used instead.

VariablesNone
 
 


<io:body reader="<%= request.getReader() %>"/>

get Availability: 

Returns the bean or bean property and pipes it into its parent PipeConsumer tag.

Tag BodyJSP    
Restrictions

AttributesNameRequired Runtime Expression Evaluation Availability
 name  Yes   Yes  
 

The name of the attribute to look up

 property  No   Yes  
 

The optional bean property name

VariablesNone
 
 


<io:get name="something" property"bar"/>

Examples

See the example application io-examples.war for examples of the usage of the tags from this custom tag library.

Java Docs

Java programmers can view the java class documentation for this tag library as javadocs.

Revision History

Review the complete revision history of this tag library.