/* * Copyright (c) 2014-2016, 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.parameter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; import org.primeframework.mvc.action.ActionInvocation; import org.primeframework.mvc.action.ActionInvocationStore; import org.primeframework.mvc.action.config.ActionConfiguration; import org.primeframework.mvc.config.MVCConfiguration; import org.primeframework.mvc.message.MessageStore; import org.primeframework.mvc.message.MessageType; import org.primeframework.mvc.message.SimpleFieldMessage; import org.primeframework.mvc.message.l10n.MessageProvider; import org.primeframework.mvc.message.l10n.MissingMessageException; import org.primeframework.mvc.parameter.ParameterParser.Parameters; import org.primeframework.mvc.parameter.ParameterParser.Parameters.Struct; import org.primeframework.mvc.parameter.convert.ConversionException; import org.primeframework.mvc.parameter.el.BeanExpressionException; import org.primeframework.mvc.parameter.el.ExpressionEvaluator; import org.primeframework.mvc.parameter.el.ExpressionException; import org.primeframework.mvc.parameter.fileupload.FileInfo; import org.primeframework.mvc.parameter.fileupload.annotation.FileUpload; import org.primeframework.mvc.util.ArrayBuilder; import org.primeframework.mvc.util.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; /** * This class is the default parameter handler. It sets all of the parameters into the action in the following order * (while invoking the correct methods in the order): * <p> * <ol> * <li>Set pre-parameters</li> * <li>Invoke pre-parameters methods</li> * <li>Set optional parameters</li> * <li>Set required parameters</li> * <li>Set files</li> * </ol> * If the action has post-parameter methods, they are handled in a subsequent workflow. * * @author Brian Pontarelli */ public class DefaultParameterHandler implements ParameterHandler { private final static Logger logger = LoggerFactory.getLogger(DefaultParameterHandler.class); private final ActionInvocationStore actionInvocationStore; private final MVCConfiguration configuration; private final ExpressionEvaluator expressionEvaluator; private final MessageProvider messageProvider; private final MessageStore messageStore; @Inject public DefaultParameterHandler(MVCConfiguration configuration, ActionInvocationStore actionInvocationStore, ExpressionEvaluator expressionEvaluator, MessageProvider messageProvider, MessageStore messageStore) { this.configuration = configuration; this.actionInvocationStore = actionInvocationStore; this.expressionEvaluator = expressionEvaluator; this.messageProvider = messageProvider; this.messageStore = messageStore; } @Override public void handle(Parameters parameters) { if (logger.isDebugEnabled()) { logger.debug("Parameters found: " + parameters); } ActionInvocation actionInvocation = actionInvocationStore.getCurrent(); Object action = actionInvocation.action; // First, process the pre-parameters setValues(parameters.pre, action, true); // Next, invoke pre methods ActionConfiguration actionConfiguration = actionInvocation.configuration; if (actionConfiguration.preParameterMethods.size() > 0) { ReflectionUtils.invokeAll(action, actionConfiguration.preParameterMethods); } // Next, process the optional setValues(parameters.optional, action, true); // Next, process the required setValues(parameters.required, action, configuration.allowUnknownParameters()); // Next, process the files if (parameters.files.size() > 0) { handleFiles(parameters.files, actionConfiguration, action); } } /** * Sets the files into the action. * * @param fileInfos The file info. * @param action The action. */ protected void handleFiles(Map<String, List<FileInfo>> fileInfos, ActionConfiguration actionConfiguration, Object action) { long maxSize = configuration.fileUploadMaxSize(); String[] allowedContentTypes = configuration.fileUploadAllowedTypes(); // Set the files into the action for (String key : fileInfos.keySet()) { // Verify file sizes and types List<FileInfo> list = new ArrayList<FileInfo>(fileInfos.get(key)); FileUpload fileUpload = actionConfiguration.fileUploadMembers.get(key); for (Iterator<FileInfo> i = list.iterator(); i.hasNext(); ) { FileInfo info = i.next(); // Check the size if (fileUpload != null && fileUpload.maxSize() != -1) { maxSize = fileUpload.maxSize(); } long fileSize = info.file.length(); if (fileSize > maxSize) { String code = "[fileUploadTooBig]" + key; String message = messageProvider.getMessage(code, key, fileSize, maxSize); messageStore.add(new SimpleFieldMessage(MessageType.ERROR, key, code, message)); i.remove(); } // Check the content type if (fileUpload != null && fileUpload.contentTypes().length > 0) { allowedContentTypes = fileUpload.contentTypes(); } String contentType = info.contentType; if (!ArrayUtils.contains(allowedContentTypes, contentType)) { String code = "[fileUploadBadContentType]" + key; String message = messageProvider.getMessage(code, key, contentType, allowedContentTypes); messageStore.add(new SimpleFieldMessage(MessageType.ERROR, key, code, message)); i.remove(); } } if (list.size() > 0) { // Set the files into the property expressionEvaluator.setValue(key, action, list); } } } /** * Sets the given values into the action. * * @param values The value mapping. * @param action The action. * @param allowUnknownParameters Whether or not invalid parameters should throw an exception or just be ignored and * log a fine message. */ protected void setValues(Map<String, Struct> values, Object action, boolean allowUnknownParameters) { for (String key : values.keySet()) { Struct struct = values.get(key); // If there are no values to set, skip it if (struct.values == null) { continue; } try { expressionEvaluator.setValue(key, action, struct.values, struct.attributes); } catch (ConversionException ce) { addCouldNotConvertMessage(key, struct, ce); } catch (BeanExpressionException ee) { throw ee; } catch (ExpressionException ee) { if (!allowUnknownParameters) { throw ee; } logger.debug("Invalid parameter to action [" + action.getClass().getName() + "]", ee); } } } private void addCouldNotConvertMessage(String key, Struct struct, ConversionException ce) { String code = "[couldNotConvert]" + key; try { String message = messageProvider.getMessage(code, (Object[]) new ArrayBuilder<>(String.class, key).addAll(struct.values).done()); messageStore.add(new SimpleFieldMessage(MessageType.ERROR, key, code, message)); } catch (MissingMessageException mme) { messageStore.add(new SimpleFieldMessage(MessageType.ERROR, key, code, ce.getMessage())); } } }