Russell Bateman
January 2018
last update:
This is hastily excerpted code from a project and is incomplete. I use it only for reference and comparison. (See also here.)
We're going to write code for three different APIs:
Let's pretend we need a client that always posts one kind of data and queries that kind of data from a server via fairly static URLs. We wrote the code for these three, different clients in a way to be able to switch between them for benchmarking.
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
public interface HttpClientOperations
{
Integer QUERY_TIMEOUT = 1000; // milliseconds
String ACCEPT_CHARSET = "Accept-Charset";
String UTF8 = "UTF-8";
String postRecord( final String record ) throws IOException;
InputStream getQuery ( final String query ) throws IOException;
}
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.slf4j.Logger;
public class HttpUrlOperations implements HttpClientOperations
{
private HttpURLConnection connection;
private static Logger logger;
private static String postUrlString; // something like "http://localhost:30000/cache/addrecords?store=0"
private static URL postURL;
public static void injectPostUrl( final String url ) { postUrlString = url; }
public static void injectLogger( Logger aLogger ) { logger = aLogger; }
private static class OperationsHolder
{
private static final HttpUrlOperations INSTANCE = new HttpUrlOperations( logger);
}
public static HttpUrlOperations getInstance()
{
return OperationsHolder.INSTANCE;
}
/**
* The URL is for POST operations. Note that
* connection.setRequestProperty("Connection", "Keep-Alive");
* ...is, in fact, the default so we don't need to make it explicit.
*
* @param logger if known.
*/
private HttpUrlOperations( Logger logger )
{
HttpUrlOperations.logger = logger;
try
{
postURL = new URL( postUrlString );
}
catch( IOException e )
{
postURL = null;
}
logInfo( "Using OkHttpClient client" );
}
/**
* This posts data being written to the server.
*/
public String postRecord( final String record ) throws IOException
{
if( postURL == null )
throw new IOException( "URL to post to was botched at initialization" );
connection = ( HttpURLConnection ) ( postURL.openConnection() );
connection.setRequestMethod( "POST" );
connection.setUseCaches( false );
connection.setDoOutput( true );
connection.setRequestProperty( "Accept", "application/xml,application/json" );
connection.addRequestProperty( "Content-type", "application/xml" );
try
{
DataOutputStream writer = new DataOutputStream( connection.getOutputStream() );
writer.writeBytes( record );
writer.close();
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred posting record (" + t.getMessage() + ')';
throw new IOException( message );
}
// This is where the request is actually sent. No matter what,
// get the response either to return or drain it.
InputStream inputStream = connection.getInputStream();
if( inputStream != null )
{
try
{
return StreamUtilities.fromStream( inputStream );
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred copying response (" + t.getMessage() + ')';
throw new IOException( message );
}
finally
{
inputStream.close();
}
}
InputStream errorStream = connection.getErrorStream();
try
{
// the stream must be read through, so we do that.
if( inputStream != null )
{
StreamUtilities.drainStream( inputStream, logger );
inputStream.close();
}
// do similarly for the error stream.
if( errorStream != null )
{
StreamUtilities.drainStream( errorStream, logger );
errorStream.close();
}
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred draining response and/or error (" + t.getMessage() + ')';
throw new IOException( message );
}
finally
{
if( inputStream != null )
inputStream.close();
if( errorStream != null )
errorStream.close();
}
return null;
}
/**
* This gets data from the server.
* @param query something like "http://localhost:30000/query?q=" plus the query
* @return the server's response as an InputStream.
* @throws IOException upon failure.
*/
public InputStream getQuery( final String query ) throws IOException
{
connection = ( HttpURLConnection ) ( query.openConnection() );
connection.setRequestProperty( ACCEPT_CHARSET, UTF8 );
connection.setReadTimeout( QUERY_TIMEOUT );
connection.setRequestMethod( "GET" );
connection.setUseCaches( false );
connection.setDoOutput( true );
connection.setRequestProperty( "Accept", "application/xml,application/json" );
try
{
return connection.getInputStream();
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred during query (" + t.getMessage() + ')';
throw new IOException( message );
}
}
public void injectLogging( Logger logger ) { HttpUrlOperations.logger = logger; }
private void logInfo( final String info )
{
if( logger != null )
logger.info( info );
}
}
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class ApacheClientOperations implements HttpClientOperations
{
private static Logger logger;
private static CloseableHttpClient client;
private static String postURL; // something like "http://localhost:30000/cache/addrecords?store=0"
public static void injectPostUrl( final String url ) { postURL = url; }
public static void injectLogger( Logger aLogger ) { logger = aLogger; }
private static class OperationsHolder
{
private static final ApacheClientOperations INSTANCE = new ApacheClientOperations( logger);
}
public static ApacheClientOperations getInstance()
{
return OperationsHolder.INSTANCE;
}
/**
* The URL is for POST operations.
*
* @param logger if known.
*/
private ApacheClientOperations( Logger logger )
{
ApacheClientOperations.logger = logger;
ApacheClientOperations.client = HttpClients.createDefault();
logInfo( "Using Apache HTTP client" );
}
/**
* This posts data being written to the server.
*/
@Override
public String postRecord( String record ) throws IOException
{
HttpPost post = new HttpPost( postURL );
List< NameValuePair > properties = new ArrayList<>();
properties.add( new BasicNameValuePair( "Accept", "application/xml,application/json" ) );
properties.add( new BasicNameValuePair( "Accept-Charset", "charset=utf-8" ) );
properties.add( new BasicNameValuePair( "Content-type", "application/xml" ) );
post.setEntity( new UrlEncodedFormEntity( properties ) );
CloseableHttpResponse response = client.execute( post );
HttpEntity entity = response.getEntity();
try
{
EntityUtils.consume( entity );
return null;
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred discarding response (" + t.getMessage() + ')';
throw new IOException( message );
}
finally
{
response.close();
}
}
/**
* This gets data from the server. The query is built as a URL before calling.
* @param query something like "http://localhost:30000/query?q=" plus the query
* @return the server's response as an InputStream.
* @throws IOException upon failure.
*/
@Override
public InputStream getQuery( String query ) throws IOException
{
HttpGet get = new HttpGet( query );
get.setHeader( ACCEPT_CHARSET, UTF8 );
get.setHeader( "Accept", "application/xml,application/json" );
CloseableHttpResponse response = client.execute( get );
HttpEntity entity = response.getEntity();
try
{
return entity.getContent();
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred getting query response (" + t.getMessage() + ')';
throw new IOException( message );
}
finally
{
// TODO: Ha! this renders the InputStream we want to return invalid! Whatever shall we do?
//response.close();
}
}
public void injectLogging( Logger logger ) { ApacheClientOperations.logger = logger; }
private void logInfo( final String info )
{
if( logger != null )
logger.info( info );
}
}
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class OkHttpOperations implements HttpClientOperations
{
private static Logger logger;
private static OkHttpClient okHttpClient;
private static String postURL; // something like "http://localhost:30000/cache/addrecords?store=0"
public static void injectPostUrl( final String url ) { postURL = url; }
public static void injectLogger( Logger aLogger ) { logger = aLogger; }
private static class OperationsHolder
{
private static final OkHttpOperations INSTANCE = new OkHttpOperations( logger);
}
public static OkHttpOperations getInstance()
{
return OperationsHolder.INSTANCE;
}
/**
* The URL is for POST operations.
*
* @param logger if known.
*/
private OkHttpOperations( Logger logger )
{
OkHttpOperations.logger = logger;
okHttpClient = new OkHttpClient.Builder()
.addInterceptor( new DefaultContentTypeInterceptor( "application/xml" ) )
.readTimeout( QUERY_TIMEOUT, TimeUnit.MILLISECONDS )
.build();
logInfo( "Using OkHttpClient client" );
}
private class DefaultContentTypeInterceptor implements Interceptor
{
private String contentType;
public DefaultContentTypeInterceptor( final String contentType ) { this.contentType = contentType; }
@Override
public Response intercept( Interceptor.Chain chain ) throws IOException
{
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("Content-Type", contentType )
.build();
return chain.proceed( requestWithUserAgent );
}
}
/**
* This posts data being written to the server.
*/
public String postRecord( final String record ) throws IOException
{
Request request = new Request.Builder()
.url( postURL )
.post( RequestBody.create(
MediaType.parse( "application/xml; charset=utf-8" ), record ) )
.build();
try
{
Response response = okHttpClient.newCall( request ).execute();
ResponseBody body = response.body();
return( body != null ) ? body.string() : null;
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred posting record (" + t.getMessage() + ')';
throw new IOException( message );
}
}
/**
* This gets data from the server. The query is built as a URL before calling.
* @param query something like "http://localhost:30000/query?q=" plus the query
* @return the server's response as an InputStream.
* @throws IOException upon failure.
*/
public InputStream getQuery( final String query ) throws IOException
{
try
{
Request request = new Request.Builder().url( query ).get().build();
Response response = okHttpClient.newCall( request ).execute();
ResponseBody body = response.body();
if( body == null ) // TODO: learn a lot more about OkHttpClient failures!
throw new IOException( "No response or other failure" );
return body.byteStream();
}
catch( IOException e )
{
Throwable t = e.getCause();
String message = e.getMessage() + " occurred during query (" + t.getMessage() + ')';
throw new IOException( message );
}
}
public void injectLogging( Logger logger ) { OkHttpOperations.logger = logger; }
private void logInfo( final String info )
{
if( logger != null )
logger.info( info );
}
}
Implemenation of String-from-Stream and drainStream().
import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
public class StreamUtilities
{
public static String fromStream( InputStream inputStream ) throws IOException
{
int ch;
StringBuilder sb = new StringBuilder();
while( ( ch = inputStream.read() ) != -1 )
sb.append( ( char ) ch );
return sb.toString();
}
private static final Integer DRAINBUFFERSIZE = 65536;
private static byte[] drainBuffer = new byte[ DRAINBUFFERSIZE ];
/**
* Per https://stackoverflow.com/questions/4767553/safe-use-of-httpurlconnection
* and https://stackoverflow.com/questions/9943351/httpsurlconnection-and-keep-alive,
* in order for the HttpURLConnection to remain open for continued use,
* the response must be completely drained otherwise the connection will have to
* close.
*
* We figure that the underlying HttpURLConnection layer knows when to stop
* reading and won't attempt to read too much.
*
* TODO: how to optimize this? I considered available() and skip(), but I hear
* bad things about them.
*
* @param inputStream with crap in it we don't care about, but must drain away.
*/
public static void drainStream( InputStream inputStream, Logger logger )
{
int thisRead;
long bytesRead = 0;
try
{
while( ( thisRead = inputStream.read( drainBuffer ) ) != -1 )
bytesRead += thisRead;
}
catch( IOException e )
{
if( logger != null )
logger.warn( "Failed while draining stream after reading " + bytesRead + " bytes" );
}
}
}
Some more useful code.
Useful in JUnit-testing web service (especially Jersey) end-points.
package com.windofkeltia.utilities;
import java.io.BufferedReader;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;
import static java.util.Objects.nonNull;
/**
* This was created as a testing aid. It uses a builder interface
* added to (a work in progress) as needs arise.
* @author Russell Bateman
*/
public class HttpServletRequestImpl implements HttpServletRequest
{
private final Map< String, ? > headers;
private Enumeration< String > headerNames;
private final String method;
private final String url; // protocol :// hostname:port / extra path ? query parameters
private final String protocol;
private final String hostname;
private final String portNumber;
private final String extraPath;
private final String queryString;
private Map< String, String > queryParameters;
private List< String > queryParameterNames;
protected HttpServletRequestImpl( Builder builder )
{
if( nonNull( headers = builder.headers ) )
headerNames = Collections.enumeration( headers.keySet() );
method = builder.method;
url = builder.url;
String work = url;
int colon = work.indexOf( ':' );
protocol = work.substring( 0, colon );
work = work.substring( colon+3 );
colon = work.indexOf( ':' );
hostname = work.substring( 0, colon );
work = work.substring( colon+1 );
int slash = work.indexOf( '/' );
portNumber = work.substring( 0, slash );
work = work.substring( portNumber.length() );
int question = work.indexOf( '?' );
extraPath = work.substring( 0, question );
queryString = work.substring( question+1 );
String[] parametersArray = queryString.split( "&" );
if( parametersArray.length > 0 )
{
queryParameters = new HashMap<>();
for( String parameter : parametersArray )
{
int equals = parameter.indexOf( '=' );
if( equals == -1 )
{
queryParameters.put( parameter, null );
continue;
}
queryParameters.put( parameter.substring( 0, equals ), parameter.substring( equals+1 ) );
}
if( queryParameters.size() > 0 )
{
queryParameterNames = new ArrayList<>();
queryParameterNames.addAll( queryParameters.keySet() );
}
}
}
public static class Builder
{
private Map< String, ? > headers = null;
private String method;
private String url;
public Builder headers ( Map< String, ? > headers ) { this.headers = headers; return this; }
public Builder method ( final String method ) { this.method = method; return this; }
public Builder url ( final String url ) { this.url = url; return this; }
public HttpServletRequestImpl build() { return new HttpServletRequestImpl( this ); }
}
/** Returns the value of the specified request parameter, a String. */
@Override public String getParameter( String s ) { return queryParameters.get( s ); }
@Override public Enumeration< String > getParameterNames() { return Collections.enumeration( queryParameterNames ); }
@Override public String[] getParameterValues( String s )
{
// TODO: when needed; we don't understand a parameter value as an array yet...
return new String[ 0 ];
}
@Override public Map< String, String[] > getParameterMap() { return null; }
/** Returns the value of the specified request header as a String. */
@Override public String getHeader( String s ) { return headers.get( s ).toString(); }
/** Returns the value of the specified request header as an int. */
@Override public int getIntHeader( String s ) { String value = headers.get( s ).toString(); return Integer.parseInt( value ); }
/** Returns the value of the specified request header as a long value that represents a Date object. */
@Override public long getDateHeader( String s )
{
// String value = headers.get( s ).toString();
// Date date = new Date( value );
return 0L; /* TODO: finish as soon as really needed... */
}
/** Returns an enumeration of all the header names this request contains. */
@Override public Enumeration< String > getHeaderNames() { return headerNames; }
/** Returns all the values of the specified request header as an Enumeration of String objects. */
@Override public Enumeration< String > getHeaders( String s )
{
List< String > values = new ArrayList<>( headers.size() );
for( Map.Entry< String, ? > element : headers.entrySet() )
{
Object value = element.getValue();
if( value instanceof String )
{
values.add( ( String ) value );
continue;
}
// TODO: this may need some investigation when 'value' isn't a String...
values.add( value.toString() );
}
return Collections.enumeration( values );
}
@Override public String getAuthType() { return null; /* if not authenticated */ }
@Override public Cookie[] getCookies() { return new Cookie[ 0 ]; }
@Override public String getMethod() { return method; }
@Override public String getPathInfo() { return extraPath; }
@Override public String getPathTranslated() { return extraPath; }
@Override public String getContextPath()
{
int slash = extraPath.indexOf( '/', 1 );
return ( slash == -1 ) ? extraPath : extraPath.substring( 0, slash );
}
@Override public String getQueryString() { return queryString; }
@Override public String getRemoteUser() { return null; }
@Override public boolean isUserInRole( String s ) { return false; }
@Override public Principal getUserPrincipal() { return null; }
@Override public String getRequestedSessionId() { return null; }
@Override public String getRequestURI() { return url; }
@Override public StringBuffer getRequestURL() { return null; }
@Override public String getServletPath() { return null; }
@Override public HttpSession getSession( boolean b ) { return null; }
@Override public HttpSession getSession() { return null; }
@Override public String changeSessionId() { return null; }
@Override public boolean isRequestedSessionIdValid() { return false; }
@Override public boolean isRequestedSessionIdFromCookie() { return false; }
@Override public boolean isRequestedSessionIdFromURL() { return false; }
@Override public boolean isRequestedSessionIdFromUrl() { return false; }
@Override public boolean authenticate( HttpServletResponse httpServletResponse ) { return false; }
@Override public void login( String s, String s1 ) { }
@Override public void logout() { }
@Override public Collection<Part> getParts() { return null; }
@Override public Part getPart( String s ) { return null; }
@Override public <T extends HttpUpgradeHandler> T upgrade( Class<T> aClass ) { return null; }
@Override public Object getAttribute( String s ) { return null; }
@Override public Enumeration<String> getAttributeNames() { return null; }
@Override public String getCharacterEncoding() { return null; }
@Override public void setCharacterEncoding( String s ) { }
@Override public int getContentLength() { return 0; }
@Override public long getContentLengthLong() { return 0; }
@Override public String getContentType() { return null; }
@Override public ServletInputStream getInputStream() { return null; }
@Override public String getProtocol() { return protocol; }
@Override public String getScheme() { return null; }
@Override public String getServerName() { return hostname; }
@Override public int getServerPort() { return Integer.parseInt( portNumber ); }
@Override public BufferedReader getReader() { return null; }
@Override public String getRemoteAddr() { return null; }
@Override public String getRemoteHost() { return null; }
@Override public void setAttribute( String s, Object o ) { }
@Override public void removeAttribute( String s ) { }
@Override public Locale getLocale() { return null; }
@Override public Enumeration<Locale> getLocales() { return null; }
@Override public boolean isSecure() { return false; }
@Override public RequestDispatcher getRequestDispatcher( String s ) { return null; }
@Override public String getRealPath( String s ) { return null; }
@Override public int getRemotePort() { return 0; }
@Override public String getLocalName() { return null; }
@Override public String getLocalAddr() { return null; }
@Override public int getLocalPort() { return 0; }
@Override public ServletContext getServletContext() { return null; }
@Override public AsyncContext startAsync()
throws IllegalStateException { return null; }
@Override public AsyncContext startAsync( ServletRequest servletRequest, ServletResponse servletResponse )
throws IllegalStateException { return null; }
@Override public boolean isAsyncStarted() { return false; }
@Override public boolean isAsyncSupported() { return false; }
@Override public AsyncContext getAsyncContext() { return null; }
@Override public DispatcherType getDispatcherType() { return null; }
}
package com.windofkeltia.trials;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import static java.util.Objects.isNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import com.windofkeltia.utilities.HttpServletRequestImpl;
import com.windofkeltia.utilities.StringUtilities;
import com.windofkeltia.utilities.TestUtilities;
/**
* Vets the class above.
* @author Russell Bateman
*/
public class HttpServletRequestImplTest
{
@Rule public TestName name = new TestName();
@After public void tearDown() { }
@Before public void setUp()
{
TestUtilities.setUp( name );
Map< String, String > headerMappings = new HashMap<>( 3 );
headerMappings.put( "ContentType", "application/xml" );
headerMappings.put( "Accept", "application/json" );
request = new HttpServletRequestImpl.Builder()
.url( "http://localhost:8080/extra/path?one=this&two=that&three=the%20other%20thing" )
.headers( headerMappings )
.build();
}
private static final boolean VERBOSE = TestUtilities.VERBOSE;
private HttpServletRequest request;
@Test
public void testBasics()
{
String protocol = request.getProtocol();
String hostname = request.getServerName();
int port = request.getServerPort();
if( VERBOSE )
{
System.out.println( "Protocol:<n " + protocol );
System.out.println( "Hostname:<n " + hostname );
System.out.println( "Port:<n " + port );
}
assertEquals( protocol, "http" );
assertEquals( hostname, "localhost" );
assertEquals( port, 8080 );
String pathInfo = request.getPathInfo();
String contextPath = request.getContextPath();
String pathTranslated = request.getPathTranslated();
if( VERBOSE )
{
System.out.println( "Path info:<n " + pathInfo );
System.out.println( "Context path:<n " + contextPath );
System.out.println( "Path translated:<n " + pathTranslated );
}
assertEquals( pathInfo, "/extra/path" );
assertEquals( contextPath, "/extra" );
assertEquals( pathTranslated, "/extra/path" );
Enumeration< String > parameterNames = request.getParameterNames();
Enumeration< String > headerNames = request.getHeaderNames();
if( VERBOSE )
{
System.out.println( "Parameter names:" );
while( parameterNames.hasMoreElements() )
System.out.println( " " + parameterNames.nextElement() );
System.out.println( "Header names:" );
while( headerNames.hasMoreElements() )
System.out.println( " " + headerNames.nextElement() );
}
String accept = request.getHeader( "Accept" ); if( VERBOSE )
System.out.println( "Accept:<n <"" + accept + '<"' );
assertEquals( accept, "application/json" );
String queryString = request.getQueryString(); if( VERBOSE )
System.out.println( "Query parameters:<n <"" + queryString + '<"' );
assertEquals( queryString, "one=this&two=that&three=the%20other%20thing" );
String three = request.getParameter( "three" ); if( VERBOSE )
System.out.println( "Parameter <"three<":<n <"" + three +
'<"' );
assertEquals( three, "the%20other%20thing" );
}
/**
* Demonstrates proper way to dig through headers. Along with getHeader(),
* there exists also <tt>getIntHeader()</tt> and getDateHeader() for use
* when you know the value will be <tt>int</tt>> or Date.
*/
@Test
public void testHeaders()
{
Enumeration< String > requestHeaderNames = request.getHeaderNames();
if( isNull( requestHeaderNames ) )
fail( "There was nothing in the request headers" );
int maxNameWidth = 0;
// note that once you exhaust the enumeration, you're done--no way around this, so copy it first...
List< String > headerNames = new ArrayList<>();
while( requestHeaderNames.hasMoreElements() )
headerNames.add( requestHeaderNames.nextElement() );
// now we can make sensible and reusable use of the list of header names...
for( String name : headerNames )
maxNameWidth = Math.max( maxNameWidth, name.length() );
for( String name : headerNames )
{
String headerValue = request.getHeader( name );
if( VERBOSE )
System.out.println( " " + StringUtilities.padStringLeft( name, maxNameWidth ) + " = " + headerValue );
}
}
@Test
public void testParameters()
{
Enumeration< String > requestParameterNames = request.getParameterNames();
if( isNull( requestParameterNames ) )
fail( "There were no request parameters" );
int maxNameWidth = 0;
// note that once you exhaust the enumeration, you're done--no way around this, so copy it first...
List< String > parameterNames = new ArrayList<>();
while( requestParameterNames.hasMoreElements() )
parameterNames.add( requestParameterNames.nextElement() );
// now we can make sensible and reusable use of the list of header names...
for( String name : parameterNames )
maxNameWidth = Math.max( maxNameWidth, name.length() );
for( String name : parameterNames )
{
String value = request.getParameter( name );
if( VERBOSE )
System.out.println( " " + StringUtilities.padStringLeft( name, maxNameWidth ) + " = " + value );
}
}
}
—in case you've never messed with Enumerations collection before.
package com.windofkeltia.trials;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.NoSuchElementException;
import org.junit.Test;
/**
* Enumerations are used in HttpServletRequest. To see how they work, examine
* {@link com.windofkeltia.http.HttpServletRequestImplTest}.
* @author Russell Bateman
*/
public class EnumerationTest
{
@Test
public void test()
{
List< String > headerNames = new ArrayList<>();
headerNames.add( "ContentType" );
headerNames.add( "Accept" );
Enumeration< String > headers = Collections.enumeration( headerNames );
try
{
while( headers.hasMoreElements() )
{
System.out.println( headers.nextElement() );
}
}
catch( IllegalArgumentException e )
{
e.printStackTrace();
}
catch( NoSuchElementException e )
{
e.printStackTrace();
}
}
}