/** * Copyright (c) 2009-2011 VMware, 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 com.springsource.insight.plugin.springweb.controller; import java.util.Map; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; import com.springsource.insight.collection.DefaultOperationCollector; import com.springsource.insight.intercept.InterceptConfiguration; import com.springsource.insight.intercept.operation.Operation; import com.springsource.insight.intercept.operation.OperationMap; import com.springsource.insight.intercept.trace.FrameBuilder; import com.springsource.insight.util.ArrayUtil; import com.springsource.insight.util.MapUtil; import com.springsource.insight.util.StringFormatterUtils; import com.springsource.insight.util.StringUtil; /** * Renders any {@link Map}, {@link Model}, {@link View}, {@link ModelMap} * and/or {@link ModelAndView} attributes * * @see <A HREF="http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-arguments">Supported method argument types</A> * @see <A HREF="http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-return-types">Supported method return types</A> */ public class ControllerOperationCollector extends DefaultOperationCollector { private static final InterceptConfiguration configuration = InterceptConfiguration.getInstance(); /** * The name of the {@link OperationMap} used to encode the return value * if it is a {@link Model}, {@link ModelMap}, {@link Map} and/or {@link ModelAndView} */ public static final String RETURN_VALUE_MODEL_MAP = "returnModel"; /** * The name of the (optional) property holding the returned view name * if it is a {@link String}, {@link View} or {@link ModelAndView} */ public static final String RETURN_VALUE_VIEW_NAME = "returnView"; public ControllerOperationCollector() { super(); } @Override protected void processNormalExit(Operation op, Object returnValue) { op.putAnyNonEmpty(RETURN_VALUE_VIEW_NAME, resolveViewName(returnValue)); if (collectExtraInformation()) { collectModelInformation(op, RETURN_VALUE_MODEL_MAP, returnValue); } } /** * Checks if the value is a {@link String}, {@link View} or {@link ModelAndView} * and resolves the view's name. <B>Note:</B> for a {@link View} the simple * class name is returned as its name * * @param value The value to be checked * @return The resolved view name - <code>null</code> if none */ static final String resolveViewName(Object value) { if (value instanceof String) { return (String) value; } else if (value instanceof View) { return value.getClass().getSimpleName(); } else if (value instanceof ModelAndView) { return ((ModelAndView) value).getViewName(); } else { return null; } } /** * Goes over all the arguments until it encounters a {@link Model}, * {@link ModelMap}, {@link Map} and/or {@link ModelAndView}. If such an argument * is encountered then its contents are encoded in an {@link OperationMap} * * @param op The {@link Operation} in which to encode the model * @param modelName The name for the created {@link OperationMap} * @param args The arguments to be checked - <B>Note:</B> the <U>first</U> * argument * @return The created {@link OperationMap} - <code>null</code> if no * {@link Model}, {@link ModelMap}, {@link Map} and/or {@link ModelAndView} found. */ static final OperationMap collectModelInformation(Operation op, String modelName, Object... args) { if (ArrayUtil.length(args) <= 0) { return null; } for (Object argVal : args) { if (argVal instanceof Model) { return collectModelMapInformation(op, modelName, ((Model) argVal).asMap()); } else if (argVal instanceof ModelMap) { return collectModelMapInformation(op, modelName, (ModelMap) argVal); } else if (argVal instanceof ModelAndView) { return collectModelMapInformation(op, modelName, ((ModelAndView) argVal).getModel()); } else if (argVal instanceof Map<?, ?>) { return collectModelMapInformation(op, modelName, (Map<?, ?>) argVal); } } return null; } /** * Encodes the model attributes {@link Map} into a {@link OperationMap} * * @param op The {@link Operation} in which to encode the model * @param modelName The name for the created {@link OperationMap} * @param modelMap The model attributes {@link Map} - may be <code>null</code>/empty, * in which case an empty {@link OperationMap} is returned * @return The created {@link OperationMap} */ static final OperationMap collectModelMapInformation(Operation op, String modelName, Map<?, ?> modelMap) { return collectModelMapInformation(op.createMap(modelName), modelMap); } static final OperationMap collectModelMapInformation(OperationMap map, Map<?, ?> modelMap) { if (MapUtil.size(modelMap) <= 0) { return map; } for (Map.Entry<?, ?> ee : modelMap.entrySet()) { String key = String.valueOf(ee.getKey()); Object value = resolveCollectedValue(ee.getValue()); if (value instanceof String) { map.put(key, (String) value); } else { map.putAny(key, value); } } return map; } static final Object resolveCollectedValue(Object value) { if (StringFormatterUtils.isPrimitiveWrapper(value)) { return value; } else { return StringUtil.chopTailAndEllipsify(String.valueOf(value), StringFormatterUtils.MAX_PARAM_LENGTH); } } static final boolean collectExtraInformation() { return FrameBuilder.OperationCollectionLevel.HIGH.equals(configuration.getCollectionLevel()); } }