users@jersey.java.net

Re: [Jersey] JSONP Outputfilter

From: tarjei <tarjei_at_nu.no>
Date: Fri, 24 Jul 2009 14:05:55 +0200

Hi,
On 07/24/2009 12:12 PM, Paul Sandoz wrote:
> Hi,
>
> You code looks almost OK :-)
>
> I think you need to check:
>
> 1) If a response entity is set, if (response.getEntity() != null); and
>
> 2) If the content-type of the response is "application/x-javascript". It
> is not sufficient to check if that media type is
> acceptable because it might not be the most acceptable.
ah, of course.
>
> If you get the output "callback=(" then this can occur if a resource
> method does not return an entity (one could view the fact that finish is
> not called as a bug in this respect and it should be called regardless
> of whether an entity is present or not).
Good point.

>
>
> 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?

I change the code to this:
if ( (!
request.getAcceptableMediaTypes().contains("application/x-javascript"))
                        ||
                        // 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.

> 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? What should I do in those
situations?

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;
                }
                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;
                }
                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")

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