Hi, thanks to Pauls inputs, I got the following filter to work:
More readable url:
http://pastebin.ca/1505767
All I am wondering about now is a) If this filter should be part of some
contrib package) and b) if there is a better check than instanceof
java.util.Collection that should be used. I tried to find the check that
is used in the source, but I didn't find it.
public class JSONCallbackResponseFilter implements ContainerResponseFilter {
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
MultivaluedMap<String, Object> headers = response.getHttpHeaders();
MediaType contentType = this.getContentType(headers);
/* no entity, no jsonp response */
if (response.getEntity() == null || contentType == null ||
false == contentType.getSubtype().equals("x-javascript") ||
false == contentType.getType().equals("application")) {
return response;
}
final Object entity =response.getEntity();
//if ( entity instanceof java.util.Collection ) { // reconstruct
genereic entity
if (GenericEntity.class.isAssignableFrom(entity.getClass())) {
response.setEntity(
new JSONWithPadding(
new GenericEntity(entity, response.getEntityType())));
} else {
response.setEntity(new JSONWithPadding(entity));
}
return response;
}
private MediaType getContentType(MultivaluedMap<String, Object>
headers) {
final Object mediaTypeHeader = headers.getFirst("Content-Type");
if (mediaTypeHeader instanceof MediaType) {
return (MediaType) mediaTypeHeader;
} else if (mediaTypeHeader != null) {
return MediaType.valueOf(mediaTypeHeader.toString());
}
return null;
}
}
Regards,
Tarjei
On 07/24/2009 02:42 PM, Paul Sandoz wrote:
>
> 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
>>>> <mailto:users-unsubscribe_at_jersey.dev.java.net>
>>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>> <mailto:users-help_at_jersey.dev.java.net>
>>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>> <mailto:users-unsubscribe_at_jersey.dev.java.net>
>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>> <mailto:users-help_at_jersey.dev.java.net>
>>>
>>
>>
>> --
>> Tarjei Huse
>> Mobil: 920 63 413
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> <mailto:users-unsubscribe_at_jersey.dev.java.net>
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>> <mailto:users-help_at_jersey.dev.java.net>
>>
>
--
Tarjei Huse
Mobil: 920 63 413