/*******************************************************************************
* Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved
*
* Licensed 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.cloudifysource.rest.controllers;
import java.io.IOException;
import java.util.Arrays;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.cloudifysource.dsl.internal.CloudifyErrorMessages;
import org.cloudifysource.dsl.internal.CloudifyMessageKeys;
import org.cloudifysource.dsl.rest.AddTemplatesException;
import org.cloudifysource.dsl.rest.response.AddTemplatesStatus;
import org.cloudifysource.dsl.rest.response.Response;
import org.cloudifysource.rest.exceptions.ResourceNotFoundException;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
*
* Provides methods usefully for implementation Rest Controller <br>
* </br> e.g. <br>
* </br> getApplication(appName) get application by given application name
*
* <ul>
* <h3>possible response codes</h3>
* </ul>
* <li>200 OK – if action is successful</li> <li>4** - In case of permission problem or illegal URL</li> <li>5** - In
* case of exception or server error</li>
*
* @throws UnsupportedOperationException
* , org.cloudifysource.rest.controllers.RestErrorException
*
*
*
* <h3>Note :</h3>
* <ul>
* this class must be thread safe
* </ul>
*
* @author ahmad
* @since 2.5.0
*/
public abstract class BaseRestController {
// thread safe
// @see http://wiki.fasterxml.com/JacksonFAQ for more info.
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Logger logger = Logger.getLogger(BaseRestController.class.getName());
@Autowired(required = true)
protected MessageSource messageSource;
/**
* Handles expected exception from the controller, and wrappes it nicely with a {@link Response} object.
*
* @param response
* - the servlet response.
* @param e
* - the thrown exception.
* @throws IOException .
*/
@ExceptionHandler(AddTemplatesException.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public void handleAddTemplatesException(final HttpServletResponse response,
final AddTemplatesException e) throws IOException {
AddTemplatesStatus status = e.getAddTemplatesResponse().getStatus();
String messageId = CloudifyErrorMessages.FAILED_TO_ADD_TEMPLATES.getName();
if (status == AddTemplatesStatus.PARTIAL_FAILURE) {
messageId = CloudifyErrorMessages.PARTLY_FAILED_TO_ADD_TEMPLATES.getName();
}
String formattedMessage;
try {
formattedMessage = messageSource.getMessage(messageId, null, Locale.US);
} catch (NoSuchMessageException ne) {
formattedMessage = messageId;
}
Response<Void> finalResponse = new Response<Void>();
finalResponse.setStatus(status.getName());
finalResponse.setMessage(formattedMessage);
finalResponse.setMessageId(messageId);
finalResponse.setResponse(null);
String addTemplatesResponseAsString = OBJECT_MAPPER.writeValueAsString(e.getAddTemplatesResponse());
Logger.getLogger(BaseRestController.class.getName())
.log(Level.INFO,
"[handleAddTemplatesException] - create failed status response with verbose: "
+ addTemplatesResponseAsString);
finalResponse.setVerbose(addTemplatesResponseAsString);
String responseString = OBJECT_MAPPER.writeValueAsString(finalResponse);
response.getOutputStream().write(responseString.getBytes());
}
/**
* Handles expected exception from the controller, and wrappes it nicely with a {@link Response} object.
*
* @param response
* - the servlet response.
* @param e
* - the thrown exception.
* @throws IOException .
*/
@ExceptionHandler(RestErrorException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public void handleExpectedErrors(final HttpServletResponse response,
final RestErrorException e) throws IOException {
String messageId = (String) e.getErrorDescription().get("error");
Object[] messageArgs = (Object[]) e.getErrorDescription().get("error_args");
String formattedMessage;
try {
formattedMessage = messageSource.getMessage(messageId, messageArgs, Locale.US);
} catch (NoSuchMessageException ne) {
String args = Arrays.toString(messageArgs);
if (logger.isLoggable(Level.WARNING)) {
logger.warning("[handleResourceNotFoundException] - failed to get message from messageSource ["
+ "messageId " + messageId + " arguments " + args + "]");
}
formattedMessage = messageId + (args == null ? "" : " [" + args + "]");
}
Response<Void> finalResponse = new Response<Void>();
finalResponse.setStatus("Failed");
finalResponse.setMessage(formattedMessage);
finalResponse.setMessageId(messageId);
finalResponse.setResponse(null);
finalResponse.setVerbose(ExceptionUtils.getFullStackTrace(e));
String responseString = OBJECT_MAPPER.writeValueAsString(finalResponse);
response.getOutputStream().write(responseString.getBytes());
}
/**
* Handles expected access denied exception from the controller, and wrappes it nicely with a {@link Response}
* object.
*
* @param response
* - the servlet response.
* @param e
* - the thrown exception.
* @throws IOException .
*/
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public void handleAccessDeniedErrors(final HttpServletResponse response,
final AccessDeniedException e) throws IOException {
String messageId = CloudifyErrorMessages.NO_PERMISSION_ACCESS_DENIED.getName();
Response<Void> finalResponse = new Response<Void>();
finalResponse.setStatus("Failed");
finalResponse.setMessage(messageId + " [" + e.getMessage() + "]");
finalResponse.setMessageId(messageId);
finalResponse.setResponse(null);
finalResponse.setVerbose(ExceptionUtils.getFullStackTrace(e));
String responseString = OBJECT_MAPPER.writeValueAsString(finalResponse);
response.getOutputStream().write(responseString.getBytes());
}
/**
*
* @param response
* @param e
* @throws IOException
*/
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public void handleResourceNotFoundException(final HttpServletResponse response,
final ResourceNotFoundException e) throws IOException {
String messageId = CloudifyMessageKeys.MISSING_RESOURCE.getName();
Object[] messageArgs = new Object[] { e.getResourceDescription() };
String formattedMessage;
try {
formattedMessage = messageSource.getMessage(messageId, messageArgs, Locale.US);
} catch (NoSuchMessageException ne) {
String args = Arrays.toString(messageArgs);
if (logger.isLoggable(Level.WARNING)) {
logger.warning("[handleResourceNotFoundException] - failed to get message from messageSource ["
+ "messageId: " + messageId + " arguments: " + args + "]");
}
formattedMessage = messageId + (args == null ? "" : " [" + args + "]");
}
Response<Void> finalResponse = new Response<Void>();
finalResponse.setStatus("Failed");
finalResponse.setMessage(formattedMessage);
finalResponse.setMessageId(messageId);
finalResponse.setResponse(null);
finalResponse.setVerbose(ExceptionUtils.getFullStackTrace(e));
if (logger.isLoggable(Level.FINE)) {
logger.fine("[handleResourceNotFoundException] - update failed response [message "
+ formattedMessage + " message ID " + messageId + "]");
}
String responseString = OBJECT_MAPPER.writeValueAsString(finalResponse);
response.getOutputStream().write(responseString.getBytes());
}
/**
* Handles unexpected exceptions from the controller, and wrappes it nicely with a {@link Response} object.
*
* @param response
* - the servlet response.
* @param t
* - the thrown exception.
* @throws IOException .
*/
@ExceptionHandler(Throwable.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public void handleUnExpectedErrors(final HttpServletResponse response,
final Throwable t) throws IOException {
Object[] messageArgs = new Object[] { t.getMessage() };
String formattedMessage =
messageSource.getMessage(
CloudifyErrorMessages.GENERAL_SERVER_ERROR.getName(),
messageArgs,
Locale.US);
Response<Void> finalResponse = new Response<Void>();
finalResponse.setStatus("Failed");
finalResponse.setMessage(formattedMessage);
finalResponse.setMessageId(CloudifyErrorMessages.GENERAL_SERVER_ERROR.getName());
finalResponse.setResponse(null);
finalResponse.setVerbose(ExceptionUtils.getFullStackTrace(t));
String responseString = OBJECT_MAPPER.writeValueAsString(finalResponse);
response.getOutputStream().write(responseString.getBytes());
}
/**
* Handles unsupported operation exception from the controller, and wrappes it nicely with a {@link Response}
* object.
*
* @param response
* the servlet response.
* @param e
* the thrown exception.
* @throws IOException
* If failed to write the response.
*/
@ExceptionHandler(UnsupportedOperationException.class)
@ResponseStatus(value = HttpStatus.NOT_IMPLEMENTED)
public void handleUnsupportedOperationException(final HttpServletResponse response,
final UnsupportedOperationException e) throws IOException {
String messageId = CloudifyErrorMessages.UNSUPPORTED_OPERATION.getName();
String formattedMessage;
try {
Object[] args = { e.getMessage() };
formattedMessage = messageSource.getMessage(messageId, args, Locale.US);
} catch (NoSuchMessageException ne) {
formattedMessage = messageId;
}
Response<Void> finalResponse = new Response<Void>();
finalResponse.setStatus("Failed");
finalResponse.setMessage(formattedMessage);
finalResponse.setMessageId(messageId);
finalResponse.setResponse(null);
finalResponse.setVerbose(ExceptionUtils.getFullStackTrace(e));
String responseString = OBJECT_MAPPER.writeValueAsString(finalResponse);
response.getOutputStream().write(responseString.getBytes());
}
}