Skip to main content

Jersey streaming binary data

In the previous post I showed how you can post binary data to a jersey REST api. You can also use Jersey to serve files, although its better done by apache or nginx but sometimes you might want to serve thumbnails stored in a database out of a service and put varnish in front of the REST api to cache the thumbnails. This is just a demonstration of using jersey to serve binary data in streaming fashion.

@Path("/download-service")
public class DownaloadService extends SecureRestService {
 private static final AppLogger logger  = AppLogger.getLogger(DownaloadService.class);

 @POST
 @Produces(MediaType.APPLICATION_OCTET_STREAM)
 public StreamingOutput getThumbnail(
   @FormParam("securityKey") final String securityKey,
   @FormParam("guid") final String guid) throws JSONException {
  return new StreamingOutput() {
   @Override
   public void write(OutputStream out) throws IOException {
    try {
     if (!isAuthorized(securityKey)) {
      response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
     } else {
      //Read thumbnail out of database and dovetail both streams(IOUtils.copy) to directly stream to the response without storing them in memory.
     }
    } catch (Throwable t) {
     logger.error(t);
     response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t.getMessage());
    }
   }
  };
 }
}


To test this code you can again use Jersey client api and to verify against response status or data you can use the ClientResponse class as shown below

  Form form = new Form();
  form.add("securityKey", getSecureToken());
  form.add("guid", guid);

  ClientResponse response = webResource.path(
    "download-service").post(ClientResponse.class, form);
  Assert.assertNotEquals(HttpServletResponse.SC_NOT_AUTHORIZED, response.getStatus());

  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  IOUtils.copy(response.getEntityInputStream(), bos);
  byte[] result = bos.toByteArray();
  Assert.assertArrayEquals(expected, result);

Comments

  1. You saved my day! Many many thanks! :)

    ReplyDelete
  2. This is a very useful bit, thank you:

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    IOUtils.copy(response.getEntityInputStream(), bos);
    byte[] result = bos.toByteArray();

    ReplyDelete
  3. With "streaming" you are talking about HTTP completed upload/download to/from a server, right?! :)

    ReplyDelete
    Replies
    1. Yes complete download from a rest api. Upload is also similar.

      Delete
  4. I need to stream records as they are available. Hence i have


    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.io.Writer;

    import javax.ws.rs.core.StreamingOutput;

    import org.apache.log4j.Logger;

    public class StreamingOutputImpl extends RetrieverResource implements StreamingOutput {
    private static final Logger LOGGER = Logger.getLogger(StreamingOutputImpl.class);
    private OutputStream os;

    public StreamingOutputImpl(final RequestParameters parameters, final String basePath) {
    super(parameters, basePath);
    }

    @Override
    public void write(final OutputStream os) throws IOException {
    this.os = os;
    read();
    }

    @Override
    public void writeRecord(final GenericRecord record) {
    try {
    Writer writer = new BufferedWriter(new OutputStreamWriter(os));
    writer.write(record.toString());
    writer.flush();
    } catch (final IOException e) {
    LOGGER.error("Error while write record " + record, e);
    }
    }
    }

    Assuming i have 100 records, and writeRecord() is invoked 100 times. I see the output in browser (with @GET and @POST) only after 100 th record and not after every record.

    Any suggestions?

    ReplyDelete
  5. How to implement the client in Jersey 2?

    ReplyDelete
  6. Just return the byte[] as entity works for me.

    ReplyDelete
    Replies
    1. for documents < 2GB yes, but for streaming data (a radio program that runs 24/7 for years for example) this would not be sufficient.

      Delete
  7. ByteArrayOutputStream bos = new ByteArrayOutputStream();
    IOUtils.copy(response.getEntityInputStream(), bos);
    byte[] result = bos.toByteArray();

    YOU SIMPLY SAVED ME!!! Thanks a lot!!!

    ReplyDelete

Post a Comment

Popular posts from this blog

RabbitMQ java clients for beginners

Here is a sample of a consumer and producer example for RabbitMQ. The steps are
Download ErlangDownload Rabbit MQ ServerDownload Rabbit MQ Java client jarsCompile and run the below two class and you are done.
This sample create a Durable Exchange, Queue and a Message. You will have to start the consumer first before you start the for the first time.

For more information on AMQP, Exchanges, Queues, read this excellent tutorial
http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/

+++++++++++++++++RabbitMQProducer.java+++++++++++++++++++++++++++
import com.rabbitmq.client.Connection; import com.rabbitmq.client.Channel; import com.rabbitmq.client.*; public class RabbitMQProducer { public static void main(String []args) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setUsername("guest"); factory.setPassword("guest"); factory.setVirtualHost("/"); factory.setHost("127.0.0.1"); factory.setPort(5672); Conne…

What a rocky start to labor day weekend

Woke up by earthquake at 7:00 AM in morning and then couldn't get to sleep. I took a bath, made my tea and started checking emails and saw that after last night deployment three storage node out of 100s of nodes were running into Full GC. What was special about the 3 nodes was that each one was in a different Data centre but it was named same app02.  This got me curious I asked the node to be taken out of rotation and take a heap dump.  Yesterday night a new release has happened and I had upgraded spymemcached library version as new relic now natively supports instrumentation on it so it was a suspect. And the hunch was a bullseye, the heap dump clearly showed it taking 1.3G and full GCs were taking 6 sec but not claiming anything.



I have a quartz job in each jvm that takes a thread dump every 5 minutes and saves last 300 of them, checking few of them quickly showed a common thread among all 3 data centres. It seems there was a long running job that was trying to replicate pending…

Email slavery

It seems I have become an EmailSlave. The first half of the day is spent in just answering to emails. There are so many emails where I am copied but I need not be. There are many emails  where its a 1-2 page email and somewhere down someone says @KP please answer this.  So it seems daily my work schedule is:
Signin to newrelic and check anomalies for 15 min. Check emails related production exception report and yes there are a ton of these report daily. Need a better tool here as this model is not scalable. I need to reduce the incoming data at me to only see relevant data like what newrelic does. May be I need to create a webapp out of these emails.Check emails for next few minutes before team callsDo team callsThen again back to checking emails until a I have taken a best shot at answering everyone waiting for my reply.Attend team meetings on Tue/Thu
Being an architect and coder at heart I don't feel satisfied at end of the day if there is nothing tangible getting done at the end.…