users@jersey.java.net

Re: [Jersey] JSONP Outputfilter

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Fri, 24 Jul 2009 14:42:06 +0200

On Jul 24, 2009, at 2:05 PM, tarjei wrote:
>
>>
>>
>> It is clear from looking at the ContainerResponse class that there
>> are a
>> number of ways this can be made easier for you, such as
>> ContainerResponse.getContentType can be made public.
> So it is not possible to get the contenttype for the response atm?
>

Yes, it is. You need to copy the code from ContainerResponse:

     private MediaType getContentType() {
         final Object mediaTypeHeader =
getHttpHeaders().getFirst("Content-Type");
         if (mediaTypeHeader instanceof MediaType) {
             return (MediaType)mediaTypeHeader;
         } else if (mediaTypeHeader != null) {
             return MediaType.valueOf(mediaTypeHeader.toString());
         }

         return null;
     }



> I change the code to this:
> if ( (! request.getAcceptableMediaTypes().contains("application/x-
> javascript"))

That will not work because you need to use an instance of MediaType.


> ||
> // some other type is preferable to the client
> ! request.getAcceptableMediaTypes().get(0).equals("application/x-
> javascript")
> ) {
> return response;
> }
>
> So to at least reduce the problem.
>
> Also I check for GenericEntity:
> if (response.getEntity() != null && ! (response.getEntity()
> instanceof GenericType)) {
> response.setEntity(new JSONWithPadding(response.getEntity()));
> return response;
> }
>
> But that didn't work.
>

ContainerResponse.setEntity works as follows:

     public void setEntity(Object entity) {
         this.entity = entity;
         if (this.entity instanceof GenericEntity) {
             final GenericEntity ge = (GenericEntity)this.entity;
             this.entityType = ge.getType();
             this.entity = ge.getEntity();
         } else {
             if (entity != null)
                 this.entityType = this.entity.getClass();
         }

         checkStatusAndEntity();
     }

It deconstructs a GenericEntity into its parts.



>> It should be possible to wrap the entity around a JSONWithPadding
>> instance and then you do not need to adapt the
>> ContainerResponseWriter.
>> The following can be done for cases when GenericEntity is not used
>> (which is created when stuff like List<T> is used). So you can do:
>
> Why doesn't it work with GenericEntity?

See above.


> What should I do in those situations?
>

You can construct the GenericEntity from getEntity and getEntityType,
so if the latter is not null.


> I tried this:
> if (response.getEntity() instanceof GenericEntity) {
> GenericEntity entity = (GenericEntity<?>) response.getEntity();
> response.setEntity(new JSONWithPadding(entity.getEntity()));
> return response;
> }
>
> But that didn't work as the entity I get from the method is an
> ArrayList, i.e. not an GenericEntity yet.
>
> The filter as it stands now looks like this:
>
> public ContainerResponse filter(ContainerRequest request,
> ContainerResponse response) {
>
> if ( (! request.getAcceptableMediaTypes().contains(new
> MediaType("application","x-javascript")))

> ||
> // some other type is preferable
> ! request.getAcceptableMediaTypes().get(0).equals(new
> MediaType("application","x-javascript"))
> ) {
> log.info("Request does not contain application/x-javascript
> mediatype. returning normal requset.");
> return response;
> }

You need to change the above to check the content type. Change the
above to:

   MediaType ct = getContentType();
   if (ct == null || !ct.getSubType().equals("x-javascript") || !
ct.getType().equals("application"))) {
     return;
   }




> log.info("Response: " + response.getEntity().getClass().getName());
> if (response.getEntity() != null && ! (response.getEntity()
> instanceof GenericEntity)) {
>
> response.setEntity(new JSONWithPadding(response.getEntity()));
> return response;
> }
>
> if (response.getEntity() instanceof GenericEntity) {
> GenericEntity entity = (GenericEntity<?>) response.getEntity();
> response.setEntity(new JSONWithPadding(entity.getRawType()));
> log.info("Returning generic type");
> return response;
> }


Change the above to:

   if (response.getEntity() != null) {
     Object entity = response.getEntity();
     if (response.getEntuityType() != null) {
        entity = new GenericEntity(entity, response.getEntuityType());
     }

     response.setEntity(new JSONWithPadding(entity));
   }


> log.info("No entity returning response as is.");
> return response;
>
> }
>
>
> PS: MediaType.equals should return true strings representing
> mediatypes like this:
> type.equals("application/x-javascript")
>

Currently it only works on instances of MediaType, which is what the
contract and equals specifies in the JavaDoc.

Paul.

> kind regards,
> Tarjei
>
>> if (content type is "application/x-javascript" &&
>> response.getEntity()
>> != null) {
>> response.setEntity(new JSONWithPadding(response.getEntity());
>> }
>>
>>
>>
>> So i think you can get things to work, but in summary there also
>> need to
>> be some improvements to make this easier:
>>
>> 1) ContainerResponseWriter.finish needs to be called when there is no
>> response entity.
>>
>> 2) ContainerResponse.getContentType should be a public method.
>>
>> 3) It should be possible to get the "raw" entity that was returned
>> by a
>> resource method.
>>
>> Paul.
>>
>> On Jul 24, 2009, at 11:47 AM, tarjei wrote:
>>
>>> Hi, I'm trying to implement JSONP on top of a solution that allready
>>> produces JSON and XML. As such, I do not want to rewrite my
>>> classes to
>>> return JSONWithPadding objects.
>>>
>>> I found some information [1] saying that I can do this by
>>> implementing
>>> an extra filter that adds wraps the JSON with a callback.
>>>
>>> I've tried implementing the filter, but somehow I'm missing an
>>> important line as the filter only returns callback=( and no content
>>> when I try to use it. The filter is below. I think I'm missing just
>>> something crucial, I just do not know what :)
>>>
>>> Is there a transparent way to do this in Jersey that I do not know
>>> about?
>>>
>>>
>>> /**
>>> * See: http://n2.nabble.com/JSONP-Callback-support-tc2260544.html
>>> */
>>> public class JSONCallbackResponseFilter implements
>>> ContainerResponseFilter {
>>> Logger log = Logger.getLogger(getClass());
>>> public ContainerResponse filter(ContainerRequest request,
>>> ContainerResponse response) {
>>> if (!request.getAcceptableMediaTypes()
>>> .contains("application/x-javascript")) {
>>> log.info("Request does not contain ok mediatype. returning normal
>>> info.");
>>> return response;
>>> }
>>>
>>> MultivaluedMap<String, String> queryParamsMapMulti =
>>> request.getQueryParameters();
>>> String callback = queryParamsMapMulti.getFirst("callback");
>>> log.debug("callback=" + callback);
>>> if (callback != null) {
>>> response.setContainerResponseWriter(
>>> new JSONCallbackResponseAdapter(
>>> response.getContainerResponseWriter(),
>>> callback));
>>> }
>>> return response;
>>> }
>>>
>>> /**
>>> * * * See:
>>> *
>>> http://n2.nabble.com/-Filter--how-to-customize-context-body---td2116754.html
>>>
>>> */
>>> public static final class JSONCallbackResponseAdapter implements
>>> ContainerResponseWriter {
>>> private final ContainerResponseWriter crw;
>>>
>>> private OutputStream out;
>>> private String callback;
>>>
>>> JSONCallbackResponseAdapter(ContainerResponseWriter crw, String
>>> callback) {
>>> this.crw = crw;
>>> this.callback = callback;
>>> }
>>>
>>> public OutputStream writeStatusAndHeaders(
>>> long contentLength,
>>> ContainerResponse response) throws IOException {
>>> out = crw.writeStatusAndHeaders(-1, response);
>>>
>>> out.write((this.callback + "(").getBytes());
>>> return out;
>>> }
>>>
>>> public void finish() throws IOException {
>>> out.write(")".getBytes());
>>> }
>>> }
>>> }
>>>
>>>
>>> 1. http://n2.nabble.com/JSONP-Callback-support-tc2260544.html
>>>
>>>
>>> Kind regards,
>>> Tarjei Huse
>>> Mobil: 920 63 413
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>
>
> --
> Tarjei Huse
> Mobil: 920 63 413
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>