/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.io.rest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.ext.Provider;
import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* Static helper methods to build up JSON-like Response objects and error handling.
*
* @author Joerg Plewe
*/
@Provider
public class JSONResponse {
public static final String JSON_KEY_ERROR_MESSAGE = "message";
public static final String JSON_KEY_ERROR = "error";
public static final String JSON_KEY_HTTPCODE = "http-code";
// also dump stacktrace?
private final static boolean WITH_STACKTRACE = false;
final static Gson GSON = new GsonBuilder().setDateFormat(DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS)
.setPrettyPrinting().create();
/**
* hide ctor a bit from public
*/
JSONResponse() {
}
/**
* basic configuration of a ResponseBuilder
*
* @param status
* @return ResponseBuilder configured for "Content-Type" MediaType.APPLICATION_JSON
*/
private static ResponseBuilder response(Response.Status status) {
return Response.status(status).header("Content-Type", MediaType.APPLICATION_JSON);
}
/**
* setup JSON depending on the content
*
* @param message a message (may be null)
* @param status
* @param entity
* @param ex
* @return
*/
private static JsonElement createErrorJson(String message, Response.Status status, Object entity, Exception ex) {
JsonObject ret = new JsonObject();
JsonObject err = new JsonObject();
ret.add(JSON_KEY_ERROR, err);
err.addProperty(JSON_KEY_ERROR_MESSAGE, message);
// in case we have a http status code, report it
if (null != status) {
err.addProperty(JSON_KEY_HTTPCODE, status.getStatusCode());
}
// in case there is an entity...
if (null != entity) {
// return the existing object
ret.add("entity", GSON.toJsonTree(entity));
}
// is there an exception?
if (null != ex) {
// JSONify the Exception
JsonObject exc = new JsonObject();
err.add("exception", exc);
{
exc.addProperty("class", ex.getClass().getName());
exc.addProperty("message", ex.getMessage());
exc.addProperty("localized-message", ex.getLocalizedMessage());
exc.addProperty("cause", null != ex.getCause() ? ex.getCause().getClass().getName() : null);
if (WITH_STACKTRACE) {
exc.add("stacktrace", GSON.toJsonTree(ex.getStackTrace()));
}
}
}
return ret;
}
/**
* in case of error (404 and such)
*
* @param status
* @param errormessage
* @return Response containing a status and the errormessage in JSON format
*/
public static Response createErrorResponse(Response.Status status, String errormessage) {
return createResponse(status, null, errormessage);
}
/**
* Depending in the status, create a Response object containing either the entity alone or an error JSON
* which might hold the entity as well.
*
* @param status
* @param entity
* @param errormessage an optional error message (may be null), ignored if the status family is successful
* @return Response configure for error or success
*/
public static Response createResponse(Response.Status status, Object entity, String errormessage) {
JsonElement ret;
if (status.getFamily() == Response.Status.Family.SUCCESSFUL) {
// create non-null JsonElement if null!=entity
ret = null != entity ? GSON.toJsonTree(entity) : null;
} else {
ret = createErrorJson(errormessage, status, entity, null);
}
// configure response
ResponseBuilder rp = response(status);
if (null != ret) {
rp = rp.entity(GSON.toJson(ret));
}
return rp.build();
}
/**
* trap exceptions
*
* @author Joerg Plewe
*/
@Provider
public static class ExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
private final Logger logger = LoggerFactory.getLogger(ExceptionMapper.class);
/**
* create JSON Response
*/
@Override
public Response toResponse(Exception e) {
logger.debug("exception during REST Handling", e);
Response.Status status = Response.Status.INTERNAL_SERVER_ERROR;
// in case the Exception is a WebApplicationException, it already carries a Status
if (e instanceof WebApplicationException) {
status = (Response.Status) ((WebApplicationException) e).getResponse().getStatusInfo();
}
JsonElement ret = createErrorJson(e.getMessage(), status, null, e);
return response(status).entity(GSON.toJson(ret)).build();
}
}
}