package org.jboss.resteasy.plugins.providers.jackson;
import java.io.IOException;
import java.io.OutputStreamWriter;
import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import org.codehaus.jackson.map.ObjectMapper;
import org.jboss.resteasy.core.MediaTypeMap;
import org.jboss.resteasy.resteasy_jaxrs.i18n.*;
/**
* <p>
* JSONP is an alternative to normal AJAX requests. Instead of using a XMLHttpRequest a script tag is added to the DOM.
* The browser will call the corresponding URL and download the JavaScript. The server creates a response which looks like a
* method call. The parameter is the body of the request. The name of the method to call is normally passed as query parameter.
* The method has to be present in the current JavaScript environment.
* </p>
* <p>
* Jackson JSON processor can produce such an response. This interceptor checks if the media type is a JavaScript one if there is a query
* parameter with the method name. The default name of this query parameter is "callback". So this interceptor is compatible with
* <a href="http://api.jquery.com/jQuery.ajax/">jQuery</a>.
* </p>
*
* @author <a href="mailto:holger.morch@nokia.com">Holger Morch</a>
* @version $Revision: 1 $
* @deprecated Use resteasy-jackson2-provider
*/
@Provider
@ConstrainedTo(RuntimeType.SERVER)
@Deprecated
public class JacksonJsonpInterceptor implements WriterInterceptor{
/**
* "text/javascript" media type. Default media type of script tags.
*/
public static final MediaType TEXT_JAVASCRIPT_MEDIA_TYPE = new MediaType("text", "javascript");
/**
* "application/javascript" media type.
*/
public static final MediaType APPLICATION_JAVASCRIPT_MEDIA_TYPE = new MediaType("application", "javascript");
/**
* "text/json" media type.
*/
public static final MediaType TEXT_JSON_TYPE = new MediaType("text", "json");
/**
* "application/*+json" media type.
*/
public static final MediaType APPLICATION_PLUS_JSON_TYPE = new MediaType("application", "*+json");
/**
* Default name of the query parameter with the method name.
*/
public static final String DEFAULT_CALLBACK_QUERY_PARAMETER = "callback";
/**
* If response media type is one of this jsonp response may be created.
*/
public static final MediaTypeMap<String> jsonpCompatibleMediaTypes = new MediaTypeMap<String>();
/**
* Default {@link ObjectMapper} for type resolution. Used if none is provided by {@link Providers}.
*/
protected static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper();
static {
jsonpCompatibleMediaTypes.add(MediaType.APPLICATION_JSON_TYPE , MediaType.APPLICATION_JSON_TYPE.toString());
jsonpCompatibleMediaTypes.add(APPLICATION_JAVASCRIPT_MEDIA_TYPE, APPLICATION_JAVASCRIPT_MEDIA_TYPE.toString());
jsonpCompatibleMediaTypes.add(APPLICATION_PLUS_JSON_TYPE , APPLICATION_PLUS_JSON_TYPE.toString());
jsonpCompatibleMediaTypes.add(TEXT_JSON_TYPE , TEXT_JSON_TYPE.toString());
jsonpCompatibleMediaTypes.add(TEXT_JAVASCRIPT_MEDIA_TYPE , TEXT_JAVASCRIPT_MEDIA_TYPE.toString());
}
private UriInfo uri;
private String callbackQueryParameter = DEFAULT_CALLBACK_QUERY_PARAMETER;
/**
* The {@link ObjectMapper} used to create typing information.
*/
protected ObjectMapper objectMapper;
/**
* The {@link Providers} used to retrieve the {@link #objectMapper} from.
*/
protected Providers providers;
/**
* {@inheritDoc}
*/
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
LogMessages.LOGGER.debugf("Interceptor : %s, Method : aroundWriteTo", getClass().getName());
String function = uri.getQueryParameters().getFirst(callbackQueryParameter);
if (function != null && !function.trim().isEmpty() && !jsonpCompatibleMediaTypes.getPossible(context.getMediaType()).isEmpty()){
OutputStreamWriter writer = new OutputStreamWriter(context.getOutputStream());
writer.write(function + "(");
writer.flush();
context.proceed();
writer.write(")");
writer.flush();
} else {
context.proceed();
}
}
/**
* Search for an {@link ObjectMapper} for the given class and mediaType
*
* @param type the {@link Class} to serialize
* @param mediaType the response {@link MediaType}
* @return the {@link ObjectMapper}
*/
protected ObjectMapper getObjectMapper(Class<?> type, MediaType mediaType)
{
if (objectMapper != null) {
return objectMapper;
}
if (providers != null) {
ContextResolver<ObjectMapper> resolver = providers.getContextResolver(ObjectMapper.class, mediaType);
if (resolver == null) {
resolver = providers.getContextResolver(ObjectMapper.class, null);
}
if (resolver != null) {
return resolver.getContext(type);
}
}
return DEFAULT_MAPPER;
}
/**
* Setter used by RESTeasy to provide the {@link UriInfo}.
*
* @param uri the uri to set
*/
@Context
public void setUri(UriInfo uri) {
this.uri = uri;
}
/**
* Setter used by RESTeasy to provide the {@link Providers}
*
* @param providers
*/
@Context
public void setProviders(Providers providers) {
this.providers = providers;
}
/**
* Set an fix {@link ObjectMapper}. If this is not set {@link Providers} are used for lookup. If there are is none too, use a default one.
*
* @param objectMapper
*/
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
/**
* Get the name of the query parameter which contains the JavaScript method name. Default: callback.
*
* @return the callbackQueryParameter
*/
public String getCallbackQueryParameter() {
return callbackQueryParameter;
}
/**
* Set callback query parameter.
*
* @see #getCallbackQueryParameter()
* @param callbackQueryParameter the callbackQueryParameter to set
*/
public void setCallbackQueryParameter(String callbackQueryParameter) {
this.callbackQueryParameter = callbackQueryParameter;
}
}