> byte[] buffer = new
> byte[request.getInputStream().available()];
> request.getInputStream().read(buffer);
[...]
> However, when I try to read the input stream it is always empty.
When reading from a Socket, available() is next to useless. It can
return zero many times before actually reaching EOF on the stream, and
it will return zero if you invoke it in the short window between the
client connecting and starting to send its request.
Create a buffer that is large enough for your request. Read from the
stream and determine when you've reached the end of the request by
following the rules of the protocol (e.g. at CR LF CR LF).
/gordon

Signature
[ do not email me copies of your followups ]
g o r d o n + n e w s @ b a l d e r 1 3 . s e
> I want to build a mock web service which intercepts call to a real web
> service; this is so that I can test the code which makes the calls with
[quoted text clipped - 34 lines]
>
> Paul
Can you post the code you are trying to create via the test?, then we
can see if a mock servlet is even needed.
Andy
> I want to build a mock web service which intercepts call to a real web
> service; this is so that I can test the code which makes the calls with
> guaranteed results.
>
> To do this I had planned to use a very simple Servlet with a hashtable
> mapping requests to responses. As follows:
There are a couple problems worth mentioning here:
> byte[] buffer = new
> byte[request.getInputStream().available()];
> request.getInputStream().read(buffer);
As Gordon explained, this doesn't work. Actually, it doesn't work for
two reasons: first, available() may return something less than the
entire length of the request; and second, read(buffer) may read less
than the entire length of the buffer (it will return the number of bytes
read, but you throw away the return value).
However, you don't need to worry about the HTTP protocol and how it
indicates end of stream. The servlet API provides a proxy InputStream
that does signal EOF at the end of the request. This works regardless
of the underlying protocol. The underlying protocol is rather complex
(between requests with and without content-length headers, chunked
transfer encoding, etc.) so you're lucky to have the servlet API doing
this for you.
Some code that will work:
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[32768];
InputStream in = request.getInputStream();
int len;
while ((len = in.read(buffer)) != -1)
{
out.write(buffer, 0, len);
}
byte[] request = out.toByteArray();
> String requestString = new String(buffer);
Problem number two. You've just taken the input byte array, and
translated it to a String using the platform default character encoding.
You have no idea what the platform default character encoding actually
is. XML files have the encoding clearly indicated in the PI at the top
of the file, but you are ignoring it.
This bug is likely to lie dormant until something unusual happens, such
as a non-ASCII character in a String argument. When that does happen,
your code may break. Not the kind of bug you want.
Two options here:
1. Wrap the byte[] in a class that does it's own comparison. Something
like
public class ByteArrayKey
{
private byte[] val;
public ByteArrayKey(byte[] v)
{
this.val = v;
}
public boolean equals(Object other)
{
if (!(other instanceof ByteArrayKey)) return false;
ByteArrayKey k = (ByteArrayKey) other;
if (val.length != k.val.length) return false;
for (int i = 0; i < val.length; i++)
{
if (val[i] != k.val[i]) return false;
}
return true;
}
public int hashCode()
{
int result = 23;
for (int i = 0; i < val.length; i++)
{
result = result * 7 + val[i];
}
return result;
}
}
You can use this as a key in the hash table, and it's a straightforward
solution to the problem.
2. The other option is to use an XML parser, which actually will
determine the encoding marked in the input file and use it to interpret
the request as text. That's a bit more code, but it's a better answer
in the long term.
The reason is that there are innocuous differences that might exist that
change the wire representation of an XML document (such as a SOAP
request), but that don't actually change the meaning. The classic
example is:
<SomeTag></SomeTag>
<SomeTag/>
Also, whitespace inside element tags, whitespace before or after the
document element, and various other changes are not significant. Your
byte array comparison will fail if one of these fails, which means that
a perfectly acceptable change in a detail of the SOAP stub on the client
will cause your tests to fail. If you parse the XML, you reduce your
vulnerability to this situation.
Even beyond that, there are XML documents that are different at a low
level, but equivalent in the SOAP request that they express. This is
the case, for example, for differences in whitespace directly inside the
SOAP Envelope element (but after the start tag). Although that
whitespace will occur in the DOM, it is not significant in the
interpretation of the request. Also of not, changes in the aliases used
for various namespaces are not significant. Having a parsed
representation makes it much easier to recognize and ignore these
insignificant differences as well.
> However, when I try to read the input stream it is always empty. Am I
> going about this the right way? or is there an easier way of creating a
> mock web service - you would think it is a pretty common thing to need
> to do, but all of the texts I can find on it talk about interpreting
> the soap; which I don't need to do.
There might be a good reason that texts talk about interpreting SOAP.
Your approach isn't so much a test against a bug as a test against any
change. You'll find yourself spending a lot of time checking whether a
minor change is okay, and then fixing your code to accept it.

Signature
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.
Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
paul - 11 Nov 2005 17:23 GMT
Thanks for all your help guys.
Gordon - you certainly got me going in the right direction, but I was
not able to find the end of the request - and I don't have any control
over what the request might be.
Andrew - I definitely do need to have a mock web service for what I'm
doing. What it is is that there are many client components, each
wrapped in a JUnit test; but the real web services are live, and
contain data which anyone can alter. The tests were failing simply
because someone somewhere had tweaked with the values, so what I wanted
to do was build an interceptor which forwards the request to the real
web service the first time, but whenever the same call was received
again, then it will return a cached response. The urls to the web
services are soft, and so as the tests run during the build, the
clients are pointed at the mock web services.
Chris - I hear what you are saying about how the String handles the
duff characters, but it is only a mock web service, the worst that can
happen is that a test will fail when it shouldn't have. Good to know
though, all the same. Your ByteArrayOutputStream works a treat - thanks
very much for that.
Paul
Andrew McDonagh - 11 Nov 2005 18:40 GMT
> Thanks for all your help guys.
>
[quoted text clipped - 12 lines]
> services are soft, and so as the tests run during the build, the
> clients are pointed at the mock web services.
If you are writing a Unit test - then your class under test needs to be
simulated 'as if' a web service framework would use it or provide a mock
webserver framework for your class to call into. E.g. TDDing your own
servlet class.
When unit testing, we are not acceptance testing, so we would not write
a test that creates a client to connect to a remote webservice. We only
write tests to exercise our classes that make up our application.
We would certainly not have any multi user client invocating upon the
same mock.
Acceptance Tests (which are rarely written in xUnit - more likely in
FitNesse, Watir, Selenium, etc) test the collaboration of our classes to
see if we the feature is working.
Put another way...
Acceptance test concerns Building the Right Code
Unit Tests concerns Building the Code Right
To be a unit test - no file access, db access, network access should happen.
> Chris - I hear what you are saying about how the String handles the
> duff characters, but it is only a mock web service, the worst that can
[quoted text clipped - 3 lines]
>
> Paul