Is there a way to write data to an OutputStream object which is connected to a channel, and this channel will pass the data to a bytebuffer (preferably, direct bytebuffer)?
I have a situation where a third party function can write its output to an outputStream. I want to be able to write this data to a bytebuffer using channels.
Is it possible?
Thank you
You can easily create a class that extends OutputStream since this only requires one method to be implemented. Sample, untested code:
public final class ByteBufferOutputStream
extends OutputStream
{
private final ByteBuffer buf;
public ByteBufferOutputStream(final int size)
{
buf = ByteBuffer.allocateDirect(size);
}
#Override
public void write(final int b)
throws IOException
{
if (buf.remaining() == 0)
throw new IOException("buffer is full");
buf.put((byte) (b & 0xff));
}
}
Then just pass an instance of that class to your API. You'll also probably want to override the other write methods since ByteBuffer has dedicated methods to write byte arrays.
Related
So for a homework assignment, I have a example of how to marshal data and unmarshal.
The structure they gave us was this:
Event is an interface.
Wireformat is a class that "inherits" an Event.
WireFormatWidget is a class with the actual code that has the marshal and unmarshal.
I have separate threads that handle the sending data in byte array using TCP.
What I have an issue is that when I create a Wireformat object. I run into issue with a thread trying to marshal the data.
Exception in thread "main" java.lang.NullPointerException
at myhw.WriteFormatWidget.getBytes(WriteFormatWidget.java:38)
The interface structure defines the data as a message, a type of message as an integer, a timestamp (of what I am assuming is Date and getTime of that date), and a tracker. I am not sure what the tracker is.
I am told this structure is the best method to sending data which is why I am trying to implement this code style.
The WriteFormatWidget consist of this:
private int type;
private long timestamp;
private String identifier;
private int tracker;
So for my wireformat, I created it as a class that extends WireFormatWidget and implements Event because that was the only way Eclipse did not spit an error or suggest changing WireFormatWidget or Event.
Now when I hardcode my specific wireformat, I instantiate it and it seems to not be able to call getBytes() with the hardcoded values I uses for the same variables.
public class MyWireFormat extends WireFormatWidget implements Event {
private String identifier = "here is my custom wireformat";
....
When I print out the identifier in the getBytes in WireFormatWidget, I get null and not the expected identifier I hardcoded. So I must not be "inheriting" appropriately. What am I doing wrong?
EDIT: WireFormatWidget (given)
public class WriteFormatWidget {
private int type;
private long timestamp;
private String identifier;
private int tracker;
public byte[] getBytes() throws IOException {
byte[] marshalledBytes = null;
ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(baOutputStream));
dout.writeInt(type);
dout.writeLong(timestamp);
System.out.println("getBytes using identifier: " + identifier);
byte[] identifierBytes = identifier.getBytes();
int elementLength = identifierBytes.length;
dout.writeInt(elementLength);
dout.write(identifierBytes);
dout.writeInt(tracker);
dout.flush();
marshalledBytes = baOutputStream.toByteArray();
baOutputStream.close();
dout.close();
return marshalledBytes;
}
}
I'll save space by not posting the unmarshalling portion. But its the same thing just in reverse.
The issue I am having is printing the data from the Client-side as proof of what I am sending beforehand.
So I will perform a simple test like print the type or print the identifier. It fails and I have null.
You're not initializing WireFormatWidget#identifier. It's declared but never initialized. Add a constructor to WireFormatWidget and provide a String as the identifier.
You need to implement something that implements Serializable, or implement directly Serializable (I think is simpler).
You do not specify many things about your interface event, but probably will inherit from Serializable, at least if you are going to implement standard java serialization.
If Event implements Serializable so it is ok, otherwise if you use another serialization method you need to specify more about it.
Assuming that you implement Serializable you need to create a ByteBuffer and call to writeObject. To create the stream you can check for example Java Serializable Object to Byte Array, so joining all:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(bos);
stream.writeObject(yourinstancetoserialize);
out.flush();
byte[] yourBytes = bos.toByteArray();
...
Probably you will need to implement the writeObject directly. In that case you use the ObjectOutputStream methods to serialize the properties, check them in https://docs.oracle.com/javase/7/docs/api/java/io/ObjectOutputStream.html for example.
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
stream.writeInt(this.type);
stream.writeLong(this.timestamp);
stream.writeBytes(this.identifier); or stream.writeChars(this.identifier);
stream.writeInt(this.tracker);
...
}
Using an Interceptor in Jersey I can manipulate the Output, however, I also want to add a Header to the response which value is calculated from the result of the output.
#Sha256Sum
public class Sha256SumInterceptor implements WriterInterceptor {
public static final String SHA256_HASH_HEADER_NAME = "SHA256-SUM";
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
// Retrieve the OutputStream, read the contents and calculate the hashsum.
// Set the header value in context.
context.proceed();
}
}
However, the issue is that when I finally have read the entire stream, I'm unable to set the headers as when context.proceed is called and the contents written (and thus enabling me to do anything with it) I can no longer set the header.
My question in short: How do I capture the entire stream output as a byte[], calculate a result from the array of bytes and finally set the header in the response to the computed result? I do not want to deplete the output stream.
If you've ever worked with an AOP framework or even CDI interceptors, you'll have worked with the concept of Around-Advice or Around-Invoke, respectively. You can perform operations before and after the invocation of the advised/intercepted method. context.proceed() works the same way; it's the method invocation (or more precisely the MessageBodyWriter doing it's writing). We can perform some operations before the MessageBodyWriter does it's job, call proceed() to let the writer do it's work, then we can do some more work.
With that said, here are the steps you can take:
Hold onto the old OutputStream from the context, with context.getOutputStream()
Create a ByteArrayOutputStream and set that as the OutputStream on the context, with context.setOutputStream(baos)
Call context.proceed(). What this does is have the MessageBodyWriter write to the ByteArrayOutputStream.
Get the byte[] from the ByteArrayOutputStream with baos.toByteArray()
Checksum the byte[] and set the header
Write the byte[] to the old OutputStream.
Finally set the OutputStream on the context to the old OutputStream.
Here's the basic implementation (tested and works as expected)
#Provider
public class ChecksumInterceptor implements WriterInterceptor {
#Override
public void aroundWriteTo(WriterInterceptorContext context)
throws IOException, WebApplicationException {
OutputStream old = context.getOutputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
context.setOutputStream(buffer);
// let MessageBodyWriter do it's job
context.proceed();
// get bytes
byte[] entity = buffer.toByteArray();
String checksum = ChecksumUtil.createChecksum(entity);
context.getHeaders().putSingle("X-Checksum", checksum);
old.write(entity);
} finally {
context.setOutputStream(old);
}
}
}
I just heard of AspectJ and it doesn't look too easy to understand, so I want to know beforehand if it (or anything else) will help me with my problem or not.
I have bunch of simple POJO classes and want to write binary serializers for them but without writing Write/Read methods by hand for each class. I could've done so with help of reflection but that will affect runtime performance. I believe I need something similar to Macroses in Scala with compile-time reflection and quasiquotes.
Update:
I'm unable to use any serialization available, because I have custom binary protocol which I can't modify (online game)
Update 2:
Example POJO with it's read, write and some helper methods. Not final version, there possibly could be some annotations, for example, but general structure should be the same. I also omitted inheritance for simplicity, in reality LoginPacket extends CommandPacket class which in turn extends Packet class.
public class LoginPacket {
public short length;
public int sessionId;
public short command;
public short error;
public String reason;
private String getString(ByteBuffer data) {
short length = data.getShort();
byte[] stringData = new byte[length];
data.get(stringData);
return new String(stringData, "UTF-8");
}
private void putString(ByteBuffer data, String someString) {
data.putShort(someString.length());
byte[] stringData = someString.getBytes("UTF-8");
data.put(stringData);
}
public static LoginPacket read(ByteBuffer data) {
LoginPacker loginPacket = new LoginPacket();
loginPacket.length = data.getShort();
loginPacket.sessionId = data.getInt();
loginPacket.command = data.getShort();
loginPacket.error = data.getShort();
loginPacket.reason = getString(data);
return loginPacket;
}
public void write(ByteBuffer data) {
data.putShort(this.length);
data.putInt(this.sessionId);
data.putShort(this.command);
data.putShort(this.error);
putString(data, this.reason);
}
}
I don't think you need to use AspectJ to modify your classes. I don't see what benefits using compile team weaving would add. I would suggest having your POJOs use implements Serializableand then serialize your objects using an ObjectOutputStream.
A simple example writing an object to a file:
outputStream = new ObjectOutputStream(new FileOutputStream(filePath));
outputStream.writeObject(yourObject);
...
// do whatever else and close stream
Similar questions:
Saving to binary/serialization java
Best way to store data for your game? (Images, maps, and such)
Which decoders are safe to extend in use with a Non Blocking Datagram Channel?
Essentially, I need to go from *ByteBuff to String, which I then have code that will turn that string into an object. Also, this would need to be accomplished with a decoder. From object to string and finally back to a *ByteBuff.
I have tried extending ByteToMessageDecoder, but it seems that Netty never invokes the decode method. So I am not sure if this is mainly a problem with the Datagram Channel or a problem with my principle understanding of decoders...
Just in case here is some of my code
Initializer:
public class Initializer extends ChannelInitializer<NioDatagramChannel> {
private SimpleChannelInboundHandler<Packet> sipHandler;
public Initializer(SimpleChannelInboundHandler<Packet> handler) {
sipHandler = handler;
}
#Override
protected void initChannel(NioDatagramChannel chan) throws Exception {
ChannelPipeline pipe = chan.pipeline();
pipe.addLast("decoder", new SipDecoder());
pipe.addLast("handler", sipHandler);
pipe.addLast("encoder", new SipEncoder());
}
}
Beginning of my Decoder:
public class SipDecoder extends ByteToMessageDecoder {
private Packet sip;
#Override
protected void decode(ChannelHandlerContext context, ByteBuf byteBuf, List<Object> objects) throws Exception {
System.out.println("got hit...");
String data = new String(byteBuf.array());
sip = new Packet();
// [...]
}
}
To handle DatagramPacket's you need to use MessageToMessageDecoder as ByteToMessageDecoder only works for ByteBuf.
Is there any restriction on the minimum size of the data that should be present in ByteBuffer so that Jackson will be able to serialize it ? I get BufferUnderflowException while doing so.
But it works fine when the size of data present is large.
public class MyTest {
private static class Wrapper {
private ByteBuffer buffer;
public void setBuffer(ByteBuffer buffer) {
this.buffer = buffer;
}
public ByteBuffer getBuffer() {
return buffer;
}
}
#Test
public void fails() throws Exception {
// Fails
ByteBuffer smallBuffer = ByteBuffer.wrap("small".getBytes());
Wrapper wrapper1 = new Wrapper();
wrapper1.setBuffer(smallBuffer);
System.out.println(new ObjectMapper().writeValueAsBytes(wrapper1));
}
#Test
public void works() throws Exception {
// Works
ByteBuffer smallBuffer = ByteBuffer.wrap("larger string works, wonder why".getBytes());
Wrapper wrapper1 = new Wrapper();
wrapper1.setBuffer(smallBuffer);
System.out.println(new ObjectMapper().writeValueAsBytes(wrapper1));
}
}
Exception stack trace:
org.codehaus.jackson.map.JsonMappingException: (was java.nio.BufferUnderflowException) (through reference chain: com.test.Wrapper["buffer"]->java.nio.HeapByteBuffer["int"])
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:218)
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:183)
at org.codehaus.jackson.map.ser.std.SerializerBase.wrapAndThrow(SerializerBase.java:140)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:158)
Jackson will normally run into problems when serializing complex objects with lots of internal state and/or non-standard getter/setter methods. In general, you should always stick to pure POJOs in the object hierarchy to be serialized.
In this particular case, you've created a wrapper that contains a ByteBuffer. Well, Jackson actually tries to serialize the entire byte buffer object (not just its byte contents), and if you look under the covers its actually trying to write out all these 'properties':
[
property 'short' (via method java.nio.HeapByteBuffer#getShort),
property 'char' (via method java.nio.HeapByteBuffer#getChar),
property 'int' (via method java.nio.HeapByteBuffer#getInt),
property 'long' (via method java.nio.HeapByteBuffer#getLong),
property 'float' (via method java.nio.HeapByteBuffer#getFloat),
property 'double' (via method java.nio.HeapByteBuffer#getDouble),
property 'direct' (via method java.nio.HeapByteBuffer#isDirect),
property 'readOnly' (via method java.nio.HeapByteBuffer#isReadOnly)
]
Its just by dumb luck that the second case works (because the buffer is long enough to get all the above shown methods invoked on it without underflowing). If you want to serialize the buffer as bytes, then either:
change the wrapper property to byte[]
keep the property as ByteBuffer, but mark it as #JsonIgnoreable and provide an alternate accessor method around it that converts to a byte[]
Example of first:
class Wrapper {
final byte[] buffer;
public Wrapper(final ByteBuffer buffer) {
super();
this.buffer = buffer != null ? buffer.array() : new byte[0];
}
}