How to Mock a javax.servlet.ServletInputStream - java

I am creating some unit testing and trying to Mock out some calls. Here is what I have in my working code:
String soapRequest = (SimUtil.readInputStream(request.getInputStream())).toString();
if (soapRequest.equals("My String")) { ... }
and SimUtil.readInputSteam looks like this:
StringBuffer sb = new StringBuffer();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(inputStream));
final int buffSize = 1024;
char[] buf = new char[buffSize];
int numRead = 0;
while ((numRead = reader.read(buf)) != -1) {
String readData = String.valueOf(buf, 0, numRead);
sb.append(readData);
buf = new char[buffSize];
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
What I am trying to do is the request.getInputStream(), the stream returns certain String.
HttpServletRequest request = mock(HttpServletRequest.class);
ServletInputStream inputStream = mock(ServletInputStream.class);
when(request.getInputStream()).thenReturn(inputStream);
So This is the code I want to condition
when(inputStream.read()).thenReturn("My String".toInt());
Any Help would be greatly appreciated.

Don't mock the InputStream. Instead, transform the String to an array of bytes using the
getBytes() method. Then create a ByteArrayInputStream with the array as input, so that it returns the String when consumed, each byte at a time. Next, create a ServletInputStream that wraps a regular InputStream like the one from Spring:
public class DelegatingServletInputStream extends ServletInputStream {
private final InputStream sourceStream;
/**
* Create a DelegatingServletInputStream for the given source stream.
* #param sourceStream the source stream (never <code>null</code>)
*/
public DelegatingServletInputStream(InputStream sourceStream) {
Assert.notNull(sourceStream, "Source InputStream must not be null");
this.sourceStream = sourceStream;
}
/**
* Return the underlying source stream (never <code>null</code>).
*/
public final InputStream getSourceStream() {
return this.sourceStream;
}
public int read() throws IOException {
return this.sourceStream.read();
}
public void close() throws IOException {
super.close();
this.sourceStream.close();
}
}
and finally, the HttpServletRequest mock would return this DelegatingServletInputStream object.

Related

How to modify the request body in HttpServletRequest method using HandlerInterceptorAdapter

I tried using HttpRequestWrapper but it keeps giving me stream closed exception. Below is my HttpRequestWrapper code. I was trying to modify the request body in preHandle method. after modifying the request body I want to send it to the controller. It seems like HandlerInterceptorAdapter been called twice. In the second time it complains that the stream is closed. I've seen post related to this issue but I could not find a solution.
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
#Override public boolean isFinished() {
return false;
}
#Override public boolean isReady() {
return false;
}
#Override public void setReadListener(ReadListener readListener) {
}
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}

java.io.IOException: Stream closed in HandlerInterceptor

I have a problem with Interceptor in SpringBoot I am trying to read the body in a request at preHandle() method.
public class LogInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
System.out.println("Error reading the request body...");
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
System.out.println("Error closing bufferedReader...");
}
}
}
String body = stringBuilder.toString();
System.out.println("--Body--"+body);
}
}
This code print body correctly but when I try to made a POST petition with Postman I receive the following error.
I/O error while reading input message; nested exception is java.io.IOException: Stream closed
If I do the same petition witouth this code the petition works correctly.
Could anyone help to me ? Or said a better solution to intercept body ?

Android: Bluetooth - How to read incoming data

I have successfully paired and connected with a Bluetooth device. I am now interested in receiving all data being transferred between the 2 and seeing whats what.
I am getting the input stream from the socket and attempting to read it. I return this and just log it.
The only way I know of doing this from what I have read is just do read with a byte buffer to return an int. However I should have loads of data coming through. How can I continually read out data being transferred, and also format as bytes rather than an int.
Thanks.
Full code below:
public class ConnectThread {
private BluetoothSocketWrapper bluetoothSocket;
private BluetoothDevice device;
private boolean secure;
private BluetoothAdapter adapter;
private List<UUID> uuidCandidates;
private int candidate;
/**
* #param device the device
* #param secure if connection should be done via a secure socket
* #param adapter the Android BT adapter
* #param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
*/
public ConnectThread(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
List<UUID> uuidCandidates) {
this.device = device;
this.secure = secure;
this.adapter = adapter;
this.uuidCandidates = uuidCandidates;
if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
this.uuidCandidates = new ArrayList<UUID>();
this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
}
}
public BluetoothSocketWrapper connect() throws IOException {
boolean success = false;
while (selectSocket()) {
adapter.cancelDiscovery();
try {
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
//try the fallback
try {
bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
Thread.sleep(500);
bluetoothSocket.connect();
success = true;
break;
} catch (FallbackException e1) {
Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
} catch (InterruptedException e1) {
Log.w("BT", e1.getMessage(), e1);
} catch (IOException e1) {
Log.w("BT", "Fallback failed. Cancelling.", e1);
}
}
}
if (!success) {
throw new IOException("Could not connect to device: "+ device.getAddress());
}
receiveData(bluetoothSocket);
return bluetoothSocket;
}
private boolean selectSocket() throws IOException {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
}
bluetoothSocket = new NativeBluetoothSocket(tmp);
return true;
}
public static interface BluetoothSocketWrapper {
InputStream getInputStream() throws IOException;
OutputStream getOutputStream() throws IOException;
String getRemoteDeviceName();
void connect() throws IOException;
String getRemoteDeviceAddress();
void close() throws IOException;
BluetoothSocket getUnderlyingSocket();
}
public static class NativeBluetoothSocket implements BluetoothSocketWrapper {
private BluetoothSocket socket;
public NativeBluetoothSocket(BluetoothSocket tmp) {
this.socket = tmp;
}
#Override
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
#Override
public String getRemoteDeviceName() {
return socket.getRemoteDevice().getName();
}
#Override
public void connect() throws IOException {
socket.connect();
}
#Override
public String getRemoteDeviceAddress() {
return socket.getRemoteDevice().getAddress();
}
#Override
public void close() throws IOException {
socket.close();
}
#Override
public BluetoothSocket getUnderlyingSocket() {
return socket;
}
}
public class FallbackBluetoothSocket extends NativeBluetoothSocket {
private BluetoothSocket fallbackSocket;
public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
super(tmp);
try
{
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
}
catch (Exception e)
{
throw new FallbackException(e);
}
}
#Override
public InputStream getInputStream() throws IOException {
return fallbackSocket.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return fallbackSocket.getOutputStream();
}
#Override
public void connect() throws IOException {
fallbackSocket.connect();
}
#Override
public void close() throws IOException {
fallbackSocket.close();
}
}
public static class FallbackException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public FallbackException(Exception e) {
super(e);
}
}
public void sendData(BluetoothSocketWrapper socket, int data) throws IOException{
ByteArrayOutputStream output = new ByteArrayOutputStream(4);
output.write(data);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(output.toByteArray());
}
public int receiveData(BluetoothSocketWrapper socket) throws IOException{
byte[] buffer = new byte[256];
ByteArrayInputStream input = new ByteArrayInputStream(buffer);
InputStream inputStream = socket.getInputStream();
inputStream.read(buffer);
return input.read();
}
}
In the first place, stop using ByteArrayInputStream and ByteArrayOutputStream for more control.
If the socket sends/receives text, do this.
TO SEND:
String text = "My message";
socketOutputStream.write(text.getBytes());
TO RECEIVE:
int length = socketInputStream.read(buffer);
String text = new String(buffer, 0, length);
The socketOutputStream should be your bluetoothSocket.getOutputStream().
If the socket sends/receives large loads of data, the key is the while loop to prevent out of memory exceptions. The data will be read by chunks of (for example every 4KB of buffer size), when you choose the buffer size, consider the heap size, if you're live-streaming media, consider latency and quality too.
TO SEND:
int length;
while ((length = largeDataInputStream.read(buffer)) != -1) {
socketOutputStream.write(buffer, 0, length);
}
TO RECEIVE:
int length;
//socketInputStream never returns -1 unless connection is broken
while ((length = socketInputStream.read(buffer)) != -1) {
largeDataOutputStream.write(buffer, 0, length);
if (progress >= dataSize) {
break; //Break loop if progress reaches the limit
}
}
FAQ:
How do I get the size of receiving data? You'll have to make your own implementation to notify remote device to get ready to receive data (including file size), this will require at least a dual-socket connection (2 sockets, 1 device), for example, 1 socket for text fragments and custom commands, and 1 socket for large data, like files or streaming.
What are largeDataInputStream and largeDataOutputStream? These streams can be normal I/O streams, FileInputStream/FileOutputStream or etc.
Why the while loop for BluetoothSocket never finishes? The socket input is continuously receiving data, and the read() methods blocks itself until data is detected. To prevent blocking the code in that line, while loop must be broken.
NOTE: This answer could need an edit. I'm not a native English speaker.
Following the above advice, I am now using this code to retrieve data.
public void receiveData(BluetoothSocketWrapper socket) throws IOException{
InputStream socketInputStream = socket.getInputStream();
byte[] buffer = new byte[256];
int bytes;
// Keep looping to listen for received messages
while (true) {
try {
bytes = socketInputStream.read(buffer); //read bytes from input buffer
String readMessage = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity via handler
Log.i("logging", readMessage + "");
} catch (IOException e) {
break;
}
}
}

How to read binary data serialized via Avro using HTTP as transport?

I'm using Avro as the serialization protocol. My service is ready, every serialization/deserialization works fine in memory.
So now I'd like to test it to see if it works fine after HTTP transport.
I thought it's simple to write a method to test, but after a while, I'm not able to figure it out, here's what I've tried:
Using HttpClient:
String itemIds = "abc123";
System.out.println("itemIds are: " + itemIds + "\n\n\n");
String endpoint = "http://localhost:8080/api/v1/items?itemIds=" + URLEncoder.encode(itemIds, "UTF-8");
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(endpoint);
String USER_AGENT = "Mozilla/5.0";
request.addHeader("User-Agent", USER_AGENT);
request.addHeader("Content-Type", "avro/binary");
HttpResponse response = client.execute(request);
System.out.println("Response Code : "
+ response.getStatusLine().getStatusCode());
byte[] bytes = EntityUtils.toByteArray(response.getEntity());
SearchMaterializationDto deserializedReponse = SearchMaterializationAvroObjectSerializer.deserializeToSearchMaterialization(bytes);
System.out.println(deserializedReponse.toString());
This approach is throwing java.io.EOFException. when executing this line SearchMaterializationDto deserializedReponse = SearchMaterializationAvroObjectSerializer.deserializeToSearchMaterialization(bytes);
Here's my SearchMaterializationDto deserializedReponse = SearchMaterializationAvroObjectSerializer.deserializeToSearchMaterialization(bytes); method:
public static SearchMaterializationDto deserializeToSearchMaterialization(byte[] buffer) {
SearchMaterializationDto searchMaterializationDto = new SearchMaterializationDto();
try {
ObjectInput input = new ObjectInputStream(new ByteArrayInputStream(buffer));
searchMaterializationDto.readExternal(input);
} catch (IOException e) {
throw new RuntimeException(e);
}
return searchMaterializationDto;
}
And here's my SearchMaterializationDto.java class (listed only the invoked method):
#org.apache.avro.specific.AvroGenerated
public class SearchMaterializationDto extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
private static final org.apache.avro.io.DatumReader
READER$ = new org.apache.avro.specific.SpecificDatumReader(SCHEMA$);
#Override public void readExternal(java.io.ObjectInput in)
throws java.io.IOException {
READER$.read(this, SpecificData.getDecoder(in));
}
}
Using Avro Decoder as this example shows:
private static void decoderWay(String endpoint) throws IOException {
byte[] bytes = getBytes(endpoint);
Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);
SpecificDatumReader<SearchMaterializationDto> reader = new SpecificDatumReader<SearchMaterializationDto>(SearchMaterializationDto.getClassSchema());
SearchMaterializationDto searchMaterializationDto = reader.read(null, decoder);
}
But it's also throwing EOFException:
Exception in thread "main" java.io.EOFException
at org.apache.avro.io.BinaryDecoder.ensureBounds(BinaryDecoder.java:473)
at org.apache.avro.io.BinaryDecoder.readLong(BinaryDecoder.java:160)
at org.apache.avro.io.BinaryDecoder.doReadItemCount(BinaryDecoder.java:363)
at org.apache.avro.io.BinaryDecoder.readMapStart(BinaryDecoder.java:408)
at org.apache.avro.io.ValidatingDecoder.readMapStart(ValidatingDecoder.java:211)
at org.apache.avro.generic.GenericDatumReader.readMap(GenericDatumReader.java:308)
at org.apache.avro.generic.GenericDatumReader.readWithoutConversion(GenericDatumReader.java:177)
at org.apache.avro.specific.SpecificDatumReader.readField(SpecificDatumReader.java:116)
at org.apache.avro.generic.GenericDatumReader.readRecord(GenericDatumReader.java:230)
at org.apache.avro.generic.GenericDatumReader.readWithoutConversion(GenericDatumReader.java:174)
at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:152)
at org.apache.avro.generic.GenericDatumReader.read(GenericDatumReader.java:144)
It turns out to be my serialization/deserilization code problem. I've changed it to the following and it works:
public static byte[] serializeSearchMaterializationToByteArray(SearchMaterializationDto searchMaterializationDto) {
return avroSerialize(SearchMaterializationDto.class, searchMaterializationDto);
}
public static <T> byte[] avroSerialize(Class<T> clazz, Object object) {
byte[] ret = null;
try {
if (object == null || !(object instanceof SpecificRecord)) {
return null;
}
T record = (T) object;
ByteArrayOutputStream out = new ByteArrayOutputStream();
Encoder e = EncoderFactory.get().directBinaryEncoder(out, null);
SpecificDatumWriter<T> w = new SpecificDatumWriter<T>(clazz);
w.write(record, e);
e.flush();
ret = out.toByteArray();
} catch (IOException e) {
}
return ret;
}
public static SearchMaterializationDto deserializeToSearchMaterialization(byte[] avroBytes) {
return avroDeserialize(avroBytes, SearchMaterializationDto.class, SearchMaterializationDto.getClassSchema());
}
public static <T> T avroDeserialize(byte[] avroBytes, Class<T> clazz, Schema schema) {
T ret = null;
try {
ByteArrayInputStream in = new ByteArrayInputStream(avroBytes);
Decoder d = DecoderFactory.get().directBinaryDecoder(in, null);
SpecificDatumReader<T> reader = new SpecificDatumReader<T>(clazz);
ret = reader.read(null, d);
} catch (IOException e) {
}
return ret;
}

How to send zipped http request?

I want to send a zipped request body as a POST http request for a web-service based application. Can anybody please help me how can I send a zipped http request or how can i send a zipped request body as part of POST http request?
Edit: Adding the solution here
HttpURLConnection request = null;
StringBuilder sb = new StringBuilder(getFileAsString("TestFile.txt"));
String fileStr = getFileAsString("TestFile.txt");
HttpClient client = new HttpClient();
client.getState().setCredentials(
new AuthScope(hostip, port),
new UsernamePasswordCredentials("username", "password"));
PutMethod post = new PutMethod(url);
post.setRequestHeader("Content-Encoding", "gzip")
HTTP protocol doesn't support compressed requests (it does support compressed responses being exchanged where the client would announce its ability to handle compressed content). If you want to implement compressed requests, then such a protocol should be established between the client and your web-service that the HTTP payload is always compressed so that on the receiving side, the web service can always decompress and interpret the payload.
public static void main(String[] args) throws MessagingException,
IOException {
HttpURLConnection request = null;
try {
// Get the object of DataInputStream
StringBuilder sb = new StringBuilder(getFileAsString("TestFile.txt"));
String fileStr = getFileAsString("TestFile.txt");
System.out.println("FileData=" + sb);
HttpClient client = new HttpClient();
client.getState().setCredentials(
new AuthScope(hostip, portno),
new UsernamePasswordCredentials(username, password));
PutMethod post = new PutMethod(url);
post.setRequestHeader("Content-Encoding", "gzip");
post.setRequestHeader("Content-Type", "application/json");
post.setDoAuthentication(true);
byte b[] = getZippedString(fileStr);;
InputStream bais = new ByteArrayInputStream(b);
post.setRequestBody(bais);
try {
int status = client.executeMethod(post);
} finally {
// release any connection resources used by the method
post.releaseConnection();
}
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
}
}
I use a special servlet that decompress and compress the requests and responses
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException{
InputStream zipedStreamRequest = req.getInputStream();
String unzipJsonStr = ZipUtil.uncompressWrite(zipedStreamRequest);
System.out.println("<---- ZIP request <----");
System.out.println(unzipJsonStr);
MainHandler handler = new MainHandler();
String responseJson = handler.handle(unzipJsonStr);
System.out.println("----> ZIP response ---->");
System.out.println(responseJson);
OutputStream responseOutputStream = res.getOutputStream();
if (responseJson!=null) {
ZipUtil.compressWrite(responseJson, responseOutputStream);
}
}
then here is my ziputil class
public class ZipUtil {
private static final int NB_BYTE_BLOCK = 1024;
/**
* compress and write in into out
* #param in the stream to be ziped
* #param out the stream where to write
* #throws IOException if a read or write problem occurs
*/
private static void compressWrite(InputStream in, OutputStream out) throws IOException{
DeflaterOutputStream deflaterOutput = new DeflaterOutputStream(out);
int nBytesRead = 1;
byte[] cur = new byte[NB_BYTE_BLOCK];
while (nBytesRead>=0){
nBytesRead = in.read(cur);
byte[] curResized;
if (nBytesRead>0){
if (nBytesRead<NB_BYTE_BLOCK){
curResized = new byte[nBytesRead];
System.arraycopy(cur, 0, curResized, 0, nBytesRead);
} else {
curResized = cur;
}
deflaterOutput.write(curResized);
}
}
deflaterOutput.close();
}
/**
* compress and write the string content into out
* #param in a string, compatible with UTF8 encoding
* #param out an output stream
*/
public static void compressWrite(String in, OutputStream out){
InputStream streamToZip = null;
try {
streamToZip = new ByteArrayInputStream(in.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
ZipUtil.compressWrite(streamToZip, out);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* uncompress and write int into out
* #param in
* #param out
* #throws IOException
*/
private static void uncompressWrite(InputStream in, OutputStream out) throws IOException{
InflaterInputStream inflaterInputStream = new InflaterInputStream(in);
int nBytesRead = 1;
byte[] cur = new byte[NB_BYTE_BLOCK];
while (nBytesRead>=0){
nBytesRead = inflaterInputStream.read(cur);
byte[] curResized;
if (nBytesRead>0){
if (0<=nBytesRead && nBytesRead<NB_BYTE_BLOCK){
curResized = new byte[nBytesRead];
System.arraycopy(cur, 0, curResized, 0, nBytesRead);
} else {
curResized = cur;
}
out.write(curResized);
}
}
out.close();
}
/**
* uncompress and write in into a new string that is returned
* #param in
* #return the string represented the unziped input stream
*/
public static String uncompressWrite(InputStream in){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
uncompressWrite(in, bos);
} catch (IOException e1) {
e1.printStackTrace();
}
try {
byte[] byteArr = bos.toByteArray();
String out = new String(byteArr, "UTF-8");
return out;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}

Categories

Resources