import info.magnolia.cms.beans.config.Paragraph; import info.magnolia.cms.beans.config.ActionBasedParagraph; import info.magnolia.cms.beans.runtime.ParagraphRenderer; import info.magnolia.cms.core.Content; import info.magnolia.context.Context; import info.magnolia.context.MgnlContext; import info.magnolia.context.WebContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletException; import java.io.IOException; import java.io.Writer; import java.util.Map; import java.util.HashMap; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Constructor; import org.apache.commons.beanutils.BeanUtils; public class JspParagraphRenderer implements ParagraphRenderer { private static final Logger log = LoggerFactory.getLogger(JspParagraphRenderer.class); /** * The given content node is ignored here (except for exception messages), * since it is supposed to be stored in the context by the include tag, * and that's also how the included jsp will render it. * (this is subject to change in the future) */ public void render(Content content, Paragraph paragraph, Writer out) throws IOException { final String jspPath = paragraph.getTemplatePath(); if (jspPath == null) { throw new IllegalStateException("Unable to render paragraph " + paragraph.getName() + " in page " + content.getHandle() + ": templatePath not set."); } log.info("render() called."); final ActionResult actionResult; if (paragraph instanceof ActionBasedParagraph) { log.info("ActionBasedParagraph encountered - execute action."); ActionBasedParagraph abp = (ActionBasedParagraph) paragraph; final Class actionClass = abp.getActionClass(); if (actionClass == null) { throw new IllegalStateException("Can't render paragraph " + paragraph.getName() + " in page " + content.getHandle() + ": actionClass not set."); } actionResult = execute(actionClass, content, abp, abp.getAllowedParametersList()); } else { actionResult = null; } try { final Context ctx = MgnlContext.getInstance(); if (!(ctx instanceof WebContext)) { throw new IllegalStateException("This paragraph renderer can only be used with a WebContext"); } Object oldAction = null; Object oldActionResult = null; if (actionResult != null) { oldAction = ctx.getAttribute("action", Context.LOCAL_SCOPE); oldActionResult = ctx.getAttribute("actionResult", Context.LOCAL_SCOPE); ctx.setAttribute("action", actionResult.getActionBean(), Context.LOCAL_SCOPE); ctx.setAttribute("actionResult", actionResult.getResult(), Context.LOCAL_SCOPE); } ((WebContext) ctx).include(jspPath, out); if (actionResult != null) { ctx.setAttribute("action", oldAction, Context.LOCAL_SCOPE); ctx.setAttribute("actionResult", oldActionResult, Context.LOCAL_SCOPE); } } catch (ServletException e) { throw new RuntimeException(e); } } protected ActionResult execute(Class actionClass, Content content, ActionBasedParagraph paragraph, String[] allowedParametersList) { try { final Object actionBean = instanciate(actionClass, content, paragraph); final Map params = MgnlContext.getParameters(); if (params != null && allowedParametersList != null) { final Map filteredParams = new HashMap(); for (int i = 0; i < allowedParametersList.length; i++) { final String param = allowedParametersList[i]; filteredParams.put(param, params.get(param)); } BeanUtils.populate(actionBean, filteredParams); } final Method method = actionClass.getMethod("execute", null); final Object result = method.invoke(actionBean, null); return new ActionResult(result, actionBean); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } protected Object instanciate(Class actionClass, Content content, ActionBasedParagraph paragraph) throws InstantiationException, IllegalAccessException, InvocationTargetException { final Constructor[] constructors = actionClass.getConstructors(); for (int i = 0; i < constructors.length; i++) { final Constructor c = constructors[i]; final Class[] params = c.getParameterTypes(); if (params.length == 2 && params[0].equals(Content.class) && params[1].equals(ActionBasedParagraph.class)) { return c.newInstance(new Object[]{content, paragraph}); } } return actionClass.newInstance(); } public static final class ActionResult { private final Object result; private final Object actionBean; public ActionResult(Object result, Object actionBean) { this.result = result; this.actionBean = actionBean; } public Object getResult() { return result; } public Object getActionBean() { return actionBean; } } }