/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.rest.util;
import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
import org.apache.brooklyn.rest.domain.ApiError;
import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.net.Urls;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
import com.google.common.collect.ImmutableMap;
import com.sun.jersey.spi.container.ContainerResponse;
public class WebResourceUtils {
private static final Logger log = LoggerFactory.getLogger(WebResourceUtils.class);
/** @throws WebApplicationException with an ApiError as its body and the given status as its response code. */
public static WebApplicationException throwWebApplicationException(Response.Status status, String format, Object... args) {
String msg = String.format(format, args);
if (log.isDebugEnabled()) {
log.debug("responding {} {} ({})",
new Object[]{status.getStatusCode(), status.getReasonPhrase(), msg});
}
ApiError apiError = ApiError.builder().message(msg).errorCode(status).build();
// including a Throwable is the only way to include a message with the WebApplicationException - ugly!
throw new WebApplicationException(new Throwable(apiError.toString()), apiError.asJsonResponse());
}
/** @throws WebApplicationException With code 500 internal server error */
public static WebApplicationException serverError(String format, Object... args) {
return throwWebApplicationException(Response.Status.INTERNAL_SERVER_ERROR, format, args);
}
/** @throws WebApplicationException With code 400 bad request */
public static WebApplicationException badRequest(String format, Object... args) {
return throwWebApplicationException(Response.Status.BAD_REQUEST, format, args);
}
/** @throws WebApplicationException With code 401 unauthorized */
public static WebApplicationException unauthorized(String format, Object... args) {
return throwWebApplicationException(Response.Status.UNAUTHORIZED, format, args);
}
/** @throws WebApplicationException With code 403 forbidden */
public static WebApplicationException forbidden(String format, Object... args) {
return throwWebApplicationException(Response.Status.FORBIDDEN, format, args);
}
/** @throws WebApplicationException With code 404 not found */
public static WebApplicationException notFound(String format, Object... args) {
return throwWebApplicationException(Response.Status.NOT_FOUND, format, args);
}
/** @throws WebApplicationException With code 412 precondition failed */
public static WebApplicationException preconditionFailed(String format, Object... args) {
return throwWebApplicationException(Response.Status.PRECONDITION_FAILED, format, args);
}
public final static Map<String,com.google.common.net.MediaType> IMAGE_FORMAT_MIME_TYPES = ImmutableMap.<String, com.google.common.net.MediaType>builder()
.put("jpg", com.google.common.net.MediaType.JPEG)
.put("jpeg", com.google.common.net.MediaType.JPEG)
.put("png", com.google.common.net.MediaType.PNG)
.put("gif", com.google.common.net.MediaType.GIF)
.put("svg", com.google.common.net.MediaType.SVG_UTF_8)
.build();
public static MediaType getImageMediaTypeFromExtension(String extension) {
com.google.common.net.MediaType mime = IMAGE_FORMAT_MIME_TYPES.get(extension.toLowerCase());
if (mime==null) return null;
try {
return MediaType.valueOf(mime.toString());
} catch (Exception e) {
log.warn("Unparseable MIME type "+mime+"; ignoring ("+e+")");
Exceptions.propagateIfFatal(e);
return null;
}
}
/** as {@link #getValueForDisplay(ObjectMapper, Object, boolean, boolean)} with no mapper
* (so will only handle a subset of types) */
public static Object getValueForDisplay(Object value, boolean preferJson, boolean isJerseyReturnValue) {
return getValueForDisplay(null, value, preferJson, isJerseyReturnValue);
}
/** returns an object which jersey will handle nicely, converting to json,
* sometimes wrapping in quotes if needed (for outermost json return types);
* if json is not preferred, this simply applies a toString-style rendering */
public static Object getValueForDisplay(ObjectMapper mapper, Object value, boolean preferJson, boolean isJerseyReturnValue) {
if (preferJson) {
if (value==null) return null;
Object result = value;
// no serialization checks required, with new smart-mapper which does toString
// (note there is more sophisticated logic in git history however)
result = value;
if (isJerseyReturnValue) {
if (result instanceof String) {
// Jersey does not do json encoding if the return type is a string,
// expecting the returner to do the json encoding himself
// cf discussion at https://github.com/dropwizard/dropwizard/issues/231
result = JavaStringEscapes.wrapJavaString((String)result);
}
}
return result;
} else {
if (value==null) return "";
return value.toString();
}
}
public static String getPathFromVersionedId(String versionedId) {
if (CatalogUtils.looksLikeVersionedId(versionedId)) {
String symbolicName = CatalogUtils.getSymbolicNameFromVersionedId(versionedId);
String version = CatalogUtils.getVersionFromVersionedId(versionedId);
return Urls.encode(symbolicName) + "/" + Urls.encode(version);
} else {
return Urls.encode(versionedId);
}
}
/** Sets the {@link HttpServletResponse} target (last argument) from the given source {@link Response};
* useful in filters where we might have a {@link Response} and need to set up an {@link HttpServletResponse}.
* Similar to {@link ContainerResponse#setResponse(Response)}; nothing like that seems to be available for {@link HttpServletResponse}. */
public static void applyJsonResponse(ServletContext servletContext, Response source, HttpServletResponse target) throws IOException {
target.setStatus(source.getStatus());
target.setContentType(MediaType.APPLICATION_JSON);
target.setCharacterEncoding("UTF-8");
target.getWriter().write(BrooklynJacksonJsonProvider.findAnyObjectMapper(servletContext, null).writeValueAsString(source.getEntity()));
}
}