/*
* Copyright (c) 2001-2017, Inversoft Inc., 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.primeframework.mvc.action.result;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Inject;
import org.primeframework.mvc.PrimeException;
import org.primeframework.mvc.action.ActionInvocation;
import org.primeframework.mvc.action.ActionInvocationStore;
import org.primeframework.mvc.action.config.ActionConfiguration;
import org.primeframework.mvc.action.result.annotation.JSON;
import org.primeframework.mvc.content.json.JacksonActionConfiguration;
import org.primeframework.mvc.message.*;
import org.primeframework.mvc.message.scope.MessageScope;
import org.primeframework.mvc.parameter.el.ExpressionEvaluator;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
/**
* This result writes out Java objects to JSON using Jackson. The content type is set to 'application/json'.
*
* @author Brian Pontarelli
*/
public class JSONResult extends AbstractResult<JSON> {
private final ActionInvocationStore actionInvocationStore;
private final MessageStore messageStore;
private final ObjectMapper objectMapper;
private final HttpServletResponse response;
@Inject
public JSONResult(ExpressionEvaluator expressionEvaluator, ActionInvocationStore actionInvocationStore, MessageStore messageStore,
ObjectMapper objectMapper, HttpServletResponse response) {
super(expressionEvaluator);
this.messageStore = messageStore;
this.response = response;
this.actionInvocationStore = actionInvocationStore;
this.objectMapper = objectMapper;
}
public boolean execute(JSON json) throws IOException, ServletException {
ActionInvocation actionInvocation = actionInvocationStore.getCurrent();
Object action = actionInvocation.action;
if (action == null) {
throw new PrimeException("There is no action class and somehow we got into the JSONResult code. Bad mojo!");
}
ActionConfiguration configuration = actionInvocation.configuration;
if (configuration == null) {
throw new PrimeException("The action [" + action.getClass() + "] has no configuration. This should be impossible!");
}
// If there are error messages, put them in a well known container and render that instead of looking for the
// @JSONResponse annotation
Object jacksonObject;
Class<?> serializationView = null;
List<Message> messages = messageStore.get(MessageScope.REQUEST);
if (messages.size() > 0) {
jacksonObject = convertErrors(messages);
} else {
JacksonActionConfiguration jacksonActionConfiguration = (JacksonActionConfiguration) configuration.additionalConfiguration.get(JacksonActionConfiguration.class);
if (jacksonActionConfiguration == null || jacksonActionConfiguration.responseMember == null) {
throw new PrimeException("The action [" + action.getClass() + "] is missing a field annotated with @JSONResponse. This is used to figure out what to send back in the response.");
}
serializationView = jacksonActionConfiguration.serializationView;
jacksonObject = expressionEvaluator.getValue(jacksonActionConfiguration.responseMember, action);
if (jacksonObject == null) {
throw new PrimeException("The @JSONResponse field [" + jacksonActionConfiguration.responseMember + "] in the action [" + action.getClass() + "] is null. It cannot be null!");
}
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
if (serializationView != void.class) {
objectMapper.writerWithView(serializationView).writeValue(baos, jacksonObject);
} else {
objectMapper.writeValue(baos, jacksonObject);
}
byte[] result = baos.toByteArray();
response.setStatus(json.status());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.setContentLength(result.length);
if (isHeadRequest(actionInvocation)) {
return true;
}
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
outputStream.flush();
return true;
}
private ErrorMessages convertErrors(List<Message> messages) {
ErrorMessages errorMessages = new ErrorMessages();
for (Message message : messages) {
if (message instanceof FieldMessage) {
FieldMessage fieldMessage = (FieldMessage) message;
errorMessages.addFieldError(fieldMessage.getField(), fieldMessage.getCode(), fieldMessage.toString());
} else {
errorMessages.generalErrors.add(new ErrorMessage(message.getCode(), message.toString()));
}
}
return errorMessages;
}
}