Commits

Anonymous committed 895c773

refactored WebWorkUtil into base class, and FreemarkerWebWorkUtil, and VelocityWebWorkUtil
refactored WebWorkUtil to keep a refrence to the request and response
added FreemarkerManager as a singleton freemarker configurationand context manager - like VelocityManager
added FreemarkerResult - using FreemarkerManager as configuration and context source
refactored FreemarkerServlet - to use FreemarkerManager as configuration and context source
refactored example webapp to use external css
updated example webapp to include better freemarker example that uses freemarker result and servlet

git-svn-id: http://svn.opensymphony.com/svn/webwork/trunk@402573baa09-0c28-0410-bef9-dab3c582ae83

Comments (0)

Files changed (21)

src/etc/example/xwork.xml

         </action>
 
         <action name="FreemarkerTest" class="com.opensymphony.webwork.example.ui.SimpleCountry">
-            <result name="success" type="dispatcher">
-                <param name="location">/country.ftl</param>
-            </result>
+            <result name="success" type="freemarker">/country.ftl</result>
+        </action>
+
+        <action name="FreemarkerTest_Servlet" class="com.opensymphony.webwork.example.ui.SimpleCountry">
+            <result name="success" type="dispatcher">/country.ftl</result>
         </action>
 
         <action name="IteratorTest" class="com.opensymphony.webwork.example.IteratorExample">

src/java/com/opensymphony/webwork/util/FreemarkerWebWorkUtil.java

+/*
+ * Created on 19/04/2004
+ */
+package com.opensymphony.webwork.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+
+/**
+ * @author CameronBraid
+ */
+public class FreemarkerWebWorkUtil extends WebWorkUtil {
+
+    public FreemarkerWebWorkUtil(OgnlValueStack stack, HttpServletRequest request, HttpServletResponse response) {
+        super(stack, request, response);
+    }
+    
+    /**
+     * 
+     * the selectedList objects are matched to the list.listValue 
+     * 
+     * listKey and listValue are optional, and if not provided, the list item is used
+     * 
+     * @param selectedList the name of the action property 
+     * 			that contains the list of selected items
+     * 			or single item if its not an array or list
+     * @param list the name of the action property 
+     * 			that contains the list of selectable items
+     * @param listKey an ognl expression that is exaluated relative to the list item 
+     * 			to use as the key of the ListEntry 
+     * @param listValue an ognl expression that is exaluated relative to the list item 
+     * 			to use as the value of the ListEntry
+     * @return a List of ListEntry
+     */
+    public List makeSelectList(String selectedList, String list, String listKey, String listValue) {
+
+        List selectList = new ArrayList();
+        
+        Collection selectedItems = null;
+        
+        Object i = stack.findValue(selectedList);
+        if (i != null) {
+            if (i.getClass().isArray()) {
+                selectedItems = Arrays.asList((Object[])i);
+            } else if (i instanceof Collection) {
+                selectedItems = (Collection)i;
+            } else {
+                // treat it is a single item
+                selectedItems = new ArrayList();
+                selectedItems.add(i);
+            }
+        }
+        Collection items = (Collection) stack.findValue(list);
+
+        if (items != null) {
+            for (Iterator iter = items.iterator(); iter.hasNext();) {
+                Object element = (Object) iter.next();
+                Object key = null;
+
+                if ((listKey == null) || (listKey.length() == 0)) {
+                    key = element;
+                } else {
+                    key = ognl.findValue(listKey, element);
+                }
+
+                Object value = null;
+
+                if ((listValue == null) || (listValue.length() == 0)) {
+                    value = element;
+                } else {
+                    value = ognl.findValue(listValue, element);
+                }
+                
+                boolean isSelected = false;
+                if (value != null && selectedItems != null && selectedItems.contains(value)) {
+                    isSelected = true;
+                }
+
+                selectList.add(new ListEntry(key, value, isSelected));
+            }
+        }
+
+        return selectList;
+    }
+    
+    public Object findValue(String expression, String className) throws ClassNotFoundException {
+        return stack.findValue(expression, Class.forName(className));
+    }
+    
+}

src/java/com/opensymphony/webwork/util/ListEntry.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+/*
+ * Created on 6/09/2003
+ *
+ */
+package com.opensymphony.webwork.util;
+
+
+/**
+ * @author CameronBraid
+ *
+ */
+public class ListEntry {
+    //~ Instance fields ////////////////////////////////////////////////////////
+
+    final private Object key;
+    final private Object value;
+    final private boolean isSelected;
+
+    //~ Constructors ///////////////////////////////////////////////////////////
+
+    public ListEntry(Object key, Object value, boolean isSelected) {
+        this.key = key;
+        this.value = value;
+        this.isSelected = isSelected;
+    }
+
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public Object getKey() {
+        return key;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public boolean getIsSelected() {
+        return isSelected;
+    }
+}

src/java/com/opensymphony/webwork/util/VelocityWebWorkUtil.java

+/*
+ * Created on 19/04/2004
+ */
+package com.opensymphony.webwork.util;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+
+import com.opensymphony.util.TextUtils;
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+
+/**
+ * @author CameronBraid
+ */
+public class VelocityWebWorkUtil extends WebWorkUtil {
+
+    private Context ctx;
+    public VelocityWebWorkUtil(Context ctx, OgnlValueStack stack, HttpServletRequest request, HttpServletResponse response) {
+        super(stack, request, response);
+        this.ctx = ctx;
+    }
+
+    /**
+     */
+    public String evaluate(String expression) throws IOException, ResourceNotFoundException, MethodInvocationException, ParseErrorException {
+        CharArrayWriter writer = new CharArrayWriter();
+        Velocity.evaluate(ctx, writer, "Error parsing " + expression, expression);
+
+        return writer.toString();
+    }
+
+    public String htmlEncode(Object obj) {
+        if (obj == null) {
+            return null;
+        }
+
+        return TextUtils.htmlEncode(obj.toString());
+    }
+
+
+    public int toInt(long aLong) {
+        return (int) aLong;
+    }
+
+    public long toLong(int anInt) {
+        return (long) anInt;
+    }
+
+    public long toLong(String aLong) {
+        if (aLong == null) {
+            return 0;
+        }
+
+        return Long.parseLong(aLong);
+    }
+
+    public String toString(long aLong) {
+        return Long.toString(aLong);
+    }
+
+    public String toString(int anInt) {
+        return Integer.toString(anInt);
+    }
+
+}

src/java/com/opensymphony/webwork/util/WebWorkUtil.java

  */
 package com.opensymphony.webwork.util;
 
-import com.opensymphony.util.TextUtils;
-
-import com.opensymphony.xwork.ObjectFactory;
-import com.opensymphony.xwork.util.OgnlValueStack;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.velocity.app.Velocity;
-import org.apache.velocity.context.Context;
-import org.apache.velocity.exception.MethodInvocationException;
-import org.apache.velocity.exception.ParseErrorException;
-import org.apache.velocity.exception.ResourceNotFoundException;
-
-import java.io.*;
-
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
 import java.net.URLEncoder;
-
 import java.util.Hashtable;
 import java.util.Map;
 
-import javax.servlet.*;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponseWrapper;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.util.TextUtils;
+import com.opensymphony.webwork.views.jsp.ui.OgnlTool;
+import com.opensymphony.xwork.ObjectFactory;
+import com.opensymphony.xwork.util.OgnlValueStack;
+
 
 /**
- *        WebWork utility methods for Velocity templates
+ *        WebWork base utility class, for use in Velocity and Freemarker templates
  *
- *        @author Rickard Öberg (rickard@dreambean.com)
- *        @version $Revision$
+ * @author Rickard Öberg (rickard@dreambean.com)
+ * @author Cameron Braid
+ * @version $Revision$
  */
-public final class WebWorkUtil {
+public class WebWorkUtil {
     //~ Static fields/initializers /////////////////////////////////////////////
 
-    private static final Log log = LogFactory.getLog(WebWorkUtil.class);
+    protected static final Log log = LogFactory.getLog(WebWorkUtil.class);
 
     //~ Instance fields ////////////////////////////////////////////////////////
 
-    Map classes = new Hashtable();
-    private Context ctx;
-    private OgnlValueStack stack;
-
+    protected Map classes = new Hashtable();
+    protected OgnlValueStack stack;
+    protected HttpServletRequest request;
+    protected HttpServletResponse response;
+    protected OgnlTool ognl = OgnlTool.getInstance();
+    
     //~ Constructors ///////////////////////////////////////////////////////////
 
-    public WebWorkUtil(Context ctx, OgnlValueStack stack) {
-        this.ctx = ctx;
+    public WebWorkUtil(OgnlValueStack stack, HttpServletRequest request, HttpServletResponse response) {
         this.stack = stack;
+        this.request = request;
+        this.response = response;
     }
 
     //~ Methods ////////////////////////////////////////////////////////////////
         return ObjectFactory.getObjectFactory().buildBean(c);
     }
 
-    public String evaluate(String expression) throws IOException, ResourceNotFoundException, MethodInvocationException, ParseErrorException {
-        CharArrayWriter writer = new CharArrayWriter();
-        Velocity.evaluate(ctx, writer, "Error parsing " + expression, expression);
-
-        return writer.toString();
-    }
-
     public Object findString(String name) {
         return stack.findValue(name, String.class);
     }
 
-    public String htmlEncode(Object obj) {
-        if (obj == null) {
-            return null;
-        }
-
-        return TextUtils.htmlEncode(obj.toString());
+    public String include(Object aName) throws Exception {
+        return include(aName, request, response);
     }
-
-    public String include(Object aName, ServletRequest aRequest, ServletResponse aResponse) throws Exception {
+    
+    /**
+     * @deprecated the request and response are stored in this util class, please use include(string)
+     */
+    public String include(Object aName, HttpServletRequest aRequest, HttpServletResponse aResponse) throws Exception {
         try {
             RequestDispatcher dispatcher = aRequest.getRequestDispatcher(aName.toString());
 
                 throw new IllegalArgumentException("Cannot find included file " + aName);
             }
 
-            ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) aResponse);
+            ResponseWrapper responseWrapper = new ResponseWrapper(aResponse);
 
             dispatcher.include(aRequest, responseWrapper);
 
         return TextUtils.plainTextToHtml(s);
     }
 
-    public int toInt(long aLong) {
-        return (int) aLong;
-    }
-
-    public long toLong(int anInt) {
-        return (long) anInt;
-    }
-
-    public long toLong(String aLong) {
-        if (aLong == null) {
-            return 0;
-        }
-
-        return Long.parseLong(aLong);
-    }
-
-    public String toString(long aLong) {
-        return Long.toString(aLong);
-    }
-
-    public String toString(int anInt) {
-        return Integer.toString(anInt);
-    }
-
     public String urlEncode(String s) {
         return URLEncoder.encode(s);
     }
             stream.write(aByte);
         }
     }
+    
 }

src/java/com/opensymphony/webwork/views/freemarker/FreemarkerManager.java

+/*
+ * Created on 18/04/2004
+ */
+package com.opensymphony.webwork.views.freemarker;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.opensymphony.util.FileManager;
+import com.opensymphony.webwork.config.Configuration;
+import com.opensymphony.webwork.util.FreemarkerWebWorkUtil;
+import com.opensymphony.webwork.views.jsp.ui.OgnlTool;
+import com.opensymphony.xwork.Action;
+import com.opensymphony.xwork.ObjectFactory;
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+import freemarker.cache.ClassTemplateLoader;
+import freemarker.cache.MultiTemplateLoader;
+import freemarker.cache.TemplateLoader;
+import freemarker.cache.WebappTemplateLoader;
+import freemarker.ext.beans.BeansWrapper;
+import freemarker.ext.jsp.TaglibFactory;
+import freemarker.ext.servlet.HttpRequestHashModel;
+import freemarker.ext.servlet.HttpSessionHashModel;
+import freemarker.ext.servlet.ServletContextHashModel;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateExceptionHandler;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateModel;
+
+
+/**
+ * 
+ * Static Configuration Manager for the FreemarkerResult's configuration
+ * 
+ * 
+ * @author CameronBraid
+ */
+public class FreemarkerManager {
+
+    private static final Log log = LogFactory.getLog(FreemarkerManager.class);
+
+    public static final String CONFIG_SERVLET_CONTEXT_KEY = "freemarker.Configuration";
+
+    public static final String KEY_REQUEST = "req";
+    public static final String KEY_RESPONSE = "res";
+    public static final String KEY_STACK = "stack";
+    public static final String KEY_OGNL = "ognl";
+    public static final String KEY_UTIL = "wwUtil";
+    public static final String KEY_WEBWORK = "webwork";
+    public static final String KEY_EXCEPTION = "exception";
+    public static final String KEY_ACTION = "action";
+
+    // coppied from freemarker servlet - since they are private
+    private static final String ATTR_APPLICATION_MODEL = ".freemarker.Application";
+    private static final String ATTR_JSP_TAGLIBS_MODEL = ".freemarker.JspTaglibs";
+    private static final String ATTR_SESSION_MODEL = ".freemarker.Session";
+    private static final String ATTR_REQUEST_MODEL = ".freemarker.Request";
+
+    // coppied from freemarker servlet - so that there is no dependency on it
+    public static final String KEY_APPLICATION = "Application";
+    public static final String KEY_REQUEST_MODEL = "Request";
+    public static final String KEY_SESSION_MODEL = "Session";
+    public static final String KEY_JSP_TAGLIBS = "JspTaglibs";
+
+	private static FreemarkerManager instance = null;
+	
+	/**
+	 * To allow for custom configuration of freemarker, sublcass this class "ConfigManager" and
+	 * set the webwork configuration property
+	 * <b>webwork.freemarker.configmanager.classname</b> to the fully qualified classname.
+	 *
+	 * This allows you to override the protected methods in the ConfigMangaer 
+	 * to programatically create your own Configuration instance
+	 *  
+	 */
+	public final static synchronized FreemarkerManager getInstance() {
+	    if (instance == null) {
+	        String classname = FreemarkerManager.class.getName();
+
+	        if (Configuration.isSet("webwork.freemarker.manager.classname")) {
+	            classname = Configuration.getString("webwork.freemarker.manager.classname").trim();
+            }
+
+            try {
+                log.info("Instantiating Freemarker ConfigManager!, " + classname);
+                instance = (FreemarkerManager) ObjectFactory.getObjectFactory().buildBean(Class.forName(classname));
+            } catch (Exception e) {
+                log.fatal("Fatal exception occurred while trying to instantiate a Freemarker ConfigManager instance, " + classname, e);
+            }
+        }
+
+	    // if the instance creation failed, make sure there is a default instance
+	    if (instance == null) {
+	        instance = new FreemarkerManager();
+	    }
+	    return instance;
+    }
+
+	/**
+	 * create the instance of the freemarker Configuration object
+	 * 
+	 * this implementation 
+	 * <ul>
+	 * 	<li>obtains the default configuration from Configuration.getDefaultConfiguration()
+	 *  <li>sets up template loading from a ClassTemplateLoader and a WebappTemplateLoader
+	 * 	<li>sets up the object wrapper to be the BeansWrapper
+	 * 	<li>loads settings from the classpath file /freemarker.properties
+	 * </ul>
+	 * 
+	 * @param servletContext
+	 * @return
+	 */
+    protected freemarker.template.Configuration createConfiguration(ServletContext servletContext) throws TemplateException {
+
+        freemarker.template.Configuration configuration = freemarker.template.Configuration.getDefaultConfiguration();
+        
+        configuration.setTemplateLoader(getTemplateLoader(servletContext));
+
+        // do this before loading the settings, because the settings may override this
+        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
+        configuration.setObjectWrapper(getObjectWrapper());	
+        
+		loadSettings(servletContext, configuration);
+        
+        return configuration;
+    }
+    
+    /**
+     * Load the settings from the /freemarker.properties file on the classpath
+     * 
+     * @see freemarker.template.Configurable#setSetting for the definition of valid settings
+     */
+    protected void loadSettings(ServletContext servletContext, freemarker.template.Configuration configuration) {
+        try {
+	        InputStream in = FileManager.loadFile("/freemarker.properties", FreemarkerManager.class);
+	        if (in != null) {
+	            configuration.setSettings(in);
+	        }
+        } catch (IOException e) {
+            log.error("Error while loading freemarker settings from /freemarker.properties", e);
+        } catch (TemplateException e) {
+            log.error("Error while loading freemarker settings from /freemarker.properties", e);
+        }
+    }
+
+    /**
+     * @return
+     */
+    protected BeansWrapper getObjectWrapper() {
+        BeansWrapper beansWrapper = new BeansWrapper();
+		beansWrapper.setSimpleMapWrapper(true);
+        return beansWrapper;
+    }
+
+    /**
+     * the default template loader is a MultiTemplateLoader which includes
+     * a ClassTemplateLoader and a WebappTemplateLoader
+     * 
+     * The ClassTemplateLoader will resolve fully qualified template includes
+     * that begin with a slash. for example /com/company/template/common.ftl
+     * 
+     * The WebappTemplateLoader attempts to resolve templates relative to the web root folder
+     */
+    protected TemplateLoader getTemplateLoader(ServletContext servletContext) {
+        // presume that most apps will require the class and webapp template loader
+        // if people wish to 
+        TemplateLoader multiLoader = new MultiTemplateLoader(
+            new TemplateLoader[] {
+                    new WebappTemplateLoader(servletContext),
+                    new ClassTemplateLoader(FreemarkerResult.class, "/")
+            });
+        return multiLoader;
+    }
+
+    public final synchronized freemarker.template.Configuration getConfigruation(ServletContext servletContext) throws TemplateException {
+        freemarker.template.Configuration config = (freemarker.template.Configuration)servletContext.getAttribute(CONFIG_SERVLET_CONTEXT_KEY);
+        if (config == null) {
+            config = createConfiguration(servletContext);
+    		// store this configuration in the servlet context 
+            servletContext.setAttribute(CONFIG_SERVLET_CONTEXT_KEY, config);
+        }
+        return config;
+    }
+    
+    public void populateContext(ScopesHashModel model, OgnlValueStack stack, Action action, HttpServletRequest request, HttpServletResponse response) {
+        // put the same objects into the context that the velocity result uses
+        model.put(KEY_REQUEST, request);
+        model.put(KEY_RESPONSE, response);
+        model.put(KEY_STACK, stack);
+        model.put(KEY_OGNL, OgnlTool.getInstance());
+        model.put(KEY_WEBWORK, new FreemarkerWebWorkUtil(stack, request, response));
+        model.put(KEY_ACTION, action);
+
+        // support for JSP exception pages, exposing the servlet or JSP exception
+        Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
+        if (exception == null) {
+            exception = (Throwable) request.getAttribute("javax.servlet.error.JspException");
+        }
+        if (exception != null) {
+            model.put(KEY_EXCEPTION, exception);
+        }
+
+    }
+    
+    public ScopesHashModel buildScopesHashModel(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, ObjectWrapper wrapper) {
+        ScopesHashModel model = new ScopesHashModel(
+                wrapper, 
+                servletContext, 
+                request);
+
+        // Create hash model wrapper for servlet context (the application)
+        // only need one thread to do this once, per servlet context
+        synchronized(servletContext) {
+	        ServletContextHashModel servletContextModel = (ServletContextHashModel) servletContext.getAttribute(ATTR_APPLICATION_MODEL);
+	        if (servletContextModel == null && servletContext.getAttribute("webwork.servlet") != null) {
+	            servletContextModel = new ServletContextHashModel((GenericServlet)servletContext.getAttribute("webwork.servlet"), wrapper);
+	            servletContext.setAttribute(ATTR_APPLICATION_MODEL,servletContextModel);
+	            TaglibFactory taglibs = new TaglibFactory(servletContext);
+	            servletContext.setAttribute(ATTR_JSP_TAGLIBS_MODEL,taglibs);
+	        }
+	        model.put(KEY_APPLICATION, servletContextModel);
+	        model.put(KEY_JSP_TAGLIBS, (TemplateModel)servletContext.getAttribute(ATTR_JSP_TAGLIBS_MODEL));
+        }
+        
+        // Create hash model wrapper for session
+        TemplateHashModel sessionModel;
+        if(request.getSession(false) != null) {
+            HttpSession session = request.getSession();
+            synchronized(session) {
+	            sessionModel = (HttpSessionHashModel) session.getAttribute(ATTR_SESSION_MODEL);
+	            if (sessionModel == null) {
+	                sessionModel = new HttpSessionHashModel(session, wrapper);
+	                session.setAttribute(ATTR_SESSION_MODEL, sessionModel);
+	            }
+            }
+            model.put(KEY_SESSION_MODEL, sessionModel);
+        }
+        else {
+            // no session means no attributes ???
+//            model.put(KEY_SESSION_MODEL, new SimpleHash());
+        }
+        
+        // Create hash model wrapper for the request
+        HttpRequestHashModel requestModel = (HttpRequestHashModel) request.getAttribute(ATTR_REQUEST_MODEL);
+        if (requestModel == null || requestModel.getRequest() != request)
+        {
+            requestModel = new HttpRequestHashModel(request, response, wrapper);
+            request.setAttribute(ATTR_REQUEST_MODEL, requestModel);
+        }
+        model.put(KEY_REQUEST_MODEL, requestModel);
+        return model;
+    }
+}

src/java/com/opensymphony/webwork/views/freemarker/FreemarkerResult.java

+/*
+ * Created on 15/04/2004
+ */
+package com.opensymphony.webwork.views.freemarker;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import com.opensymphony.webwork.ServletActionContext;
+import com.opensymphony.webwork.dispatcher.WebWorkResultSupport;
+import com.opensymphony.xwork.ActionInvocation;
+
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+
+/**
+ * @author CameronBraid
+ */
+public class FreemarkerResult extends WebWorkResultSupport {
+    
+    /*
+     * webwork results are constructed for each result execeution
+     * 
+     * the current context is availible to subclasses via these protected fields 
+     */
+    protected String location;
+    protected Configuration configuration;
+    protected ActionInvocation invocation;
+    protected ObjectWrapper wrapper;
+
+    /**
+     * allow parameterization of the contentType
+     * the default being text/html
+     */
+    public String getContentType() {
+        return pContentType;
+    }
+    private String pContentType = "text/html";
+    public void setContentType(String aContentType) {
+        pContentType = aContentType;
+    }
+
+    /**
+     * execute this result, using the specified template location.
+     * 
+     * The template location has already been interoplated for any variable substitutions
+     * 
+     * this method obtains the freemarker configuration and the object wrapper from the provided hooks. 
+     * It them implements the template processing workflow by calling the hooks for 
+     * preTemplateProcess and postTemplateProcess 
+     */
+    public void doExecute(String location, ActionInvocation invocation) throws IOException, TemplateException
+    {
+    	this.location = location;
+    	this.invocation = invocation;
+    	this.configuration = getConfiguration();
+    	this.wrapper = getObjectWrapper();
+        
+    	Template template = configuration.getTemplate(location, deduceLocale());
+        TemplateModel model = createModel();
+        
+        // Give subclasses a chance to hook into preprocessing
+        if (preTemplateProcess(template, model)) {
+            try {
+                // Process the template
+                template.process(model, getWriter());
+            } finally {
+                // Give subclasses a chance to hook into postprocessing
+                postTemplateProcess(template, model);
+            }
+        }
+    }
+
+    /**
+     * the default writer writes directly to the response output stream 
+     */
+    protected Writer getWriter() throws IOException {
+        return new OutputStreamWriter(ServletActionContext.getResponse().getOutputStream());
+    }
+    
+    /**
+     * Build the instance of the ScopesHashModel, including JspTagLib support
+     * 
+     * Objects added to the model are 
+     * 
+     * <ul>
+     *  <li>Application - servlet context attributes hash model
+     *  <li>JspTaglibs - jsp tag lib factory model
+     *  <li>Request - request attributes hash model
+     *  <li>Session - session attributes hash model
+     *  <li>req - the HttpServletRequst object for direct access
+     *  <li>res - the HttpServletResponse object for direct access
+     *  <li>stack - the OgnLValueStack instance for direct access
+     *  <li>ognl - the instance of the OgnlTool
+     *  <li>action - the action itself
+     *  <li>exception - optional : the JSP or Servlet exception as per the servlet spec (for JSP Exception pages)
+     *  <li>webwork - instance of the WebWorkUtil class
+     *</ul>
+     */
+    protected TemplateModel createModel() throws TemplateModelException{
+        
+        ServletContext servletContext = ServletActionContext.getServletContext();
+        HttpServletRequest request = ServletActionContext.getRequest();
+        HttpServletResponse response = ServletActionContext.getResponse();
+        HttpSession session = ServletActionContext.getRequest().getSession(false);
+
+        ScopesHashModel model = FreemarkerManager.getInstance()
+        	.buildScopesHashModel(servletContext, request, response, wrapper);
+        
+        FreemarkerManager.getInstance().populateContext(
+                model, 
+                invocation.getStack(), 
+                invocation.getAction(), 
+                request, 
+                response);
+
+        return new ValueStackModel(
+        	ServletActionContext.getContext().getValueStack(), 
+        	model, 
+        	wrapper);
+	}
+
+    /**
+     * the default implementation of postTemplateProcess applies the contentType parameter
+     */
+    protected void postTemplateProcess(Template template, TemplateModel data)throws IOException {
+    }
+
+    
+    /**
+     * Called before the execution is passed to template.process().
+     * This is a generic hook you might use in subclasses to perform a specific
+     * action before the template is processed. By default does nothing.
+     * A typical action to perform here is to inject application-specific
+     * objects into the model root
+     * @return true to process the template, false to suppress template processing.
+     */
+    protected boolean preTemplateProcess(Template template, TemplateModel model)
+        throws IOException
+    {
+        Object attrContentType = template.getCustomAttribute("content_type");
+        if(attrContentType != null) {
+            ServletActionContext.getResponse().setContentType(attrContentType.toString());
+        }
+        else {
+            String contentType = getContentType();
+		    if (contentType == null) {
+		        contentType = "text/html";
+		    }
+	        String encoding = template.getEncoding();
+	        if (encoding != null) {
+	            contentType = contentType + "; charset=" + encoding;
+	        }
+	        ServletActionContext.getResponse().setContentType(contentType);
+        }
+        
+        return true;
+    }
+
+    
+    /**
+     * This method is called from {@link #doExecute()} to obtain the
+     * FreeMarker configuration object that this result will use
+     * for template loading. This is a hook that allows you
+     * to custom-configure the configuration object in a subclass, 
+     * or to fetch it from an IoC container. 
+     * 
+     * <b>
+     * 	The default implementation obtains the configuration from the ConfigurationManager instance.</b>
+     * </b>
+     */
+    protected Configuration getConfiguration() throws TemplateException {
+        return FreemarkerManager.getInstance().getConfigruation(ServletActionContext.getServletContext());
+    }
+    
+    /**
+     * This method is called from {@link #doExecute()} to obtain the
+     * FreeMarker object wrapper object that this result will use
+     * for adapting objects into 
+     * template models.. This is a hook that allows you
+     * to custom-configure the wrapper object in a subclass.
+     * 
+     * <b>
+     * The default implementation returns @see Configuration#getObjectWrapper()
+     * </b>
+     */
+    protected ObjectWrapper getObjectWrapper() {
+        return configuration.getObjectWrapper();
+    }
+
+
+    /**
+     * Returns the locale used for the 
+     * {@link Configuration#getTemplate(String, Locale)} call.
+     * The base implementation simply returns the locale setting of the
+     * configuration. Override this method to provide different behaviour,
+     */
+    protected Locale deduceLocale() {
+        return configuration.getLocale();
+    }
+}

src/java/com/opensymphony/webwork/views/freemarker/FreemarkerServlet.java

  */
 package com.opensymphony.webwork.views.freemarker;
 
-import com.opensymphony.xwork.ActionContext;
-
-import freemarker.template.ObjectWrapper;
-import freemarker.template.SimpleHash;
-import freemarker.template.Template;
-import freemarker.template.TemplateHashModel;
-import freemarker.template.TemplateModel;
-import freemarker.template.TemplateModelException;
-
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.Locale;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import com.opensymphony.webwork.ServletActionContext;
+import com.opensymphony.xwork.Action;
+import com.opensymphony.xwork.util.OgnlValueStack;
+
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
 
 /**
  * @author CameronBraid
- *
+ * @deprecated Please use the FreemarkerResult result type instead
+ * 
  */
-public class FreemarkerServlet extends freemarker.ext.servlet.FreemarkerServlet {
+public class FreemarkerServlet extends HttpServlet {
     //~ Constructors ///////////////////////////////////////////////////////////
-
+    
+    protected Configuration configuration;
+    
     /**
      *
      */
 
     //~ Methods ////////////////////////////////////////////////////////////////
 
-    /* (non-Javadoc)
-    * @see freemarker.ext.servlet.FreemarkerServlet#createModel(freemarker.template.ObjectWrapper, javax.servlet.ServletContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-    */
     protected TemplateModel createModel(ObjectWrapper wrapper, ServletContext servletContext, HttpServletRequest request, HttpServletResponse response) throws TemplateModelException {
-        // get the superclasses model and wrap it in a ValueStackModelWrapper
-        TemplateHashModel model = (TemplateHashModel) super.createModel(wrapper, servletContext, request, response);
+        
+        ScopesHashModel model = FreemarkerManager.getInstance().buildScopesHashModel(servletContext, request, response, getObjectWrapper());
 
-        ValueStackModel valueStackModel = new ValueStackModel(model);
-        valueStackModel.setObjectWrapper(getObjectWrapper());
+        Action action = null;
+        if (ServletActionContext.getContext().getActionInvocation() != null) {
+            action = ServletActionContext.getContext().getActionInvocation().getAction();
+        }
+        OgnlValueStack stack = ServletActionContext.getContext().getValueStack();
+        
+        FreemarkerManager.getInstance().populateContext(model, stack, action, request, response);
 
-        return valueStackModel;
+        return new ValueStackModel(
+        	stack, 
+        	model, 
+        	wrapper);
+    }
+    
+    public void init() throws ServletException {
+        try {
+            configuration = createConfiguration();
+        } catch (TemplateException e) {
+            throw new ServletException("could not configure Freemarker", e);
+        }
+    }
+        
+    protected Configuration createConfiguration() throws TemplateException {
+        return FreemarkerManager.getInstance().getConfigruation(getServletContext());
     }
 
-    /*
-    *
-    * @see freemarker.ext.servlet.FreemarkerServlet#preTemplateProcess(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, freemarker.template.Template, freemarker.template.TemplateModel)
-    */
-    protected boolean preTemplateProcess(HttpServletRequest request, HttpServletResponse response, Template template, TemplateModel templateModel) throws ServletException, IOException {
-        super.preTemplateProcess(request, response, template, templateModel);
-
-        SimpleHash hash = (SimpleHash) templateModel;
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws ServletException, IOException
+    {
+        process(request, response);
+    }
 
-        // support for access to the value stack using the key 'stack' 
-        hash.put("stack", ActionContext.getContext().getValueStack());
+    public void doPost(
+        HttpServletRequest request,
+        HttpServletResponse response)
+        throws ServletException, IOException
+    {
+        process(request, response);
+    }
 
-        // the select list helper is a utility class to assist in building a list of key/value
-        // when given a list and a keyName and valueName, as in ww:select input
-        hash.put("wwUtil", new FreemarkerUtil());
+    private void process(
+        HttpServletRequest request,
+        HttpServletResponse response)
+        throws ServletException, IOException
+    {
 
-        // support for JSP exception pages, exposing the servlet or JSP exception
-        Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");
+        String path = requestUrlToTemplatePath(request);
 
-        if (exception == null) {
-            exception = (Throwable) request.getAttribute("javax.servlet.error.JspException");
+        Template template = null;
+        try {
+            template = configuration.getTemplate(path, deduceLocale(path, request, response));
+        } catch (FileNotFoundException e) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+        
+        Object attrContentType = template.getCustomAttribute("content_type");
+        if(attrContentType != null) {
+            response.setContentType(attrContentType.toString());
+        }
+        else {
+            response.setContentType("text/html; charset=" + template.getEncoding());
         }
 
-        if (exception != null) {
-            hash.put("exception", exception);
+//        // Set cache policy
+//        setBrowserCachingPolicy(response);
+
+        ServletContext servletContext = getServletContext();
+        try {
+            TemplateModel model = createModel(getObjectWrapper(), servletContext, request, response);
+
+            // Give subclasses a chance to hook into preprocessing
+            if (preTemplateProcess(request, response, template, model)) {
+                try {
+                    // Process the template
+                    template.process(model, response.getWriter());
+                } finally {
+                    // Give subclasses a chance to hook into postprocessing
+                    postTemplateProcess(request, response, template, model);
+                }
+            }
+        } catch (TemplateException te) {
+            throw new ServletException("Error executing FreeMarker template", te);
         }
+    }
+
+    /**
+     * Maps the request URL to a template path that is passed to 
+     * {@link Configuration#getTemplate(String, Locale)}. You can override it
+     * (i.e. to provide advanced rewriting capabilities), but you are strongly
+     * encouraged to call the overridden method first, then only modify its
+     * return value. 
+     * @param request the currently processed request
+     * @return a String representing the template path
+     */
+    protected String requestUrlToTemplatePath(HttpServletRequest request)
+    {
+        // First, see if it is an included request
+        String includeServletPath  = (String) request.getAttribute("javax.servlet.include.servlet_path");
+        if(includeServletPath != null)
+        {
+            // Try path info; only if that's null (servlet is mapped to an
+            // URL extension instead of to prefix) use servlet path.
+            String includePathInfo = (String) request.getAttribute("javax.servlet.include.path_info");
+            return includePathInfo == null ? includeServletPath : includePathInfo;
+        } 
+        // Seems that the servlet was not called as the result of a 
+        // RequestDispatcher.include(...). Try pathInfo then servletPath again,
+        // only now directly on the request object:
+        String path = request.getPathInfo();
+        if (path != null) return path;
+        path = request.getServletPath();
+        if (path != null) return path;
+        // Seems that it is a servlet mapped with prefix, and there was no extra path info.
+        return "";
+    }
+
+    /**
+     * This method is called from {@link #doExecute()} to obtain the
+     * FreeMarker object wrapper object that this result will use
+     * for adapting objects into 
+     * template models.. This is a hook that allows you
+     * to custom-configure the wrapper object in a subclass.
+     * 
+     * <b>
+     * The default implementation returns @see Configuration#getObjectWrapper()
+     * </b>
+     */
+    protected ObjectWrapper getObjectWrapper() {
+        return configuration.getObjectWrapper();
+    }
+
+//    /**
+//     * If the parameter "nocache" was set to true, generate a set of headers
+//     * that will advise the HTTP client not to cache the returned page.
+//     */
+//    private void setBrowserCachingPolicy(HttpServletResponse response)
+//    {
+//        if (nocache)
+//        {
+//            // HTTP 1.1 browsers should defeat caching on this header
+//            response.setHeader("Cache-Control", "no-cache");
+//            // HTTP 1.0 browsers should defeat caching on this header
+//            response.setHeader("Pragma", "no-cache");
+//            // Last resort for those that ignore all of the above
+//            response.setHeader("Expires", EXPIRATION_DATE);
+//        }
+//    }
+    
 
+    /**
+     * Called before the execution is passed to template.process().
+     * This is a generic hook you might use in subclasses to perform a specific
+     * action before the template is processed. By default does nothing.
+     * A typical action to perform here is to inject application-specific
+     * objects into the model root
+     * @param request the actual HTTP request
+     * @param response the actual HTTP response
+     * @param template the template that will get executed
+     * @param data the data that will be passed to the template
+     * @return true to process the template, false to suppress template processing.
+     */
+    protected boolean preTemplateProcess(
+        HttpServletRequest request,
+        HttpServletResponse response,
+        Template template,
+        TemplateModel data)
+        throws ServletException, IOException
+    {
         return true;
     }
+
+    /**
+     * Called after the execution returns from template.process().
+     * This is a generic hook you might use in subclasses to perform a specific
+     * action after the template is processed. It will be invoked even if the
+     * template processing throws an exception. By default does nothing.
+     * @param request the actual HTTP request
+     * @param response the actual HTTP response
+     * @param template the template that was executed
+     * @param data the data that was passed to the template
+     */
+    protected void postTemplateProcess(
+        HttpServletRequest request,
+        HttpServletResponse response,
+        Template template,
+        TemplateModel data)
+        throws ServletException, IOException
+    {
+    }
+
+    /**
+     * Returns the locale used for the 
+     * {@link Configuration#getTemplate(String, Locale)} call.
+     * The base implementation simply returns the locale setting of the
+     * configuration. Override this method to provide different behaviour, i.e.
+     * to use the locale indicated in the request.
+     */
+    protected Locale deduceLocale(
+            String templatePath, HttpServletRequest request, HttpServletResponse response) {
+        return configuration.getLocale();
+    }
+
 }

src/java/com/opensymphony/webwork/views/freemarker/FreemarkerUtil.java

-/*
- * Copyright (c) 2002-2003 by OpenSymphony
- * All rights reserved.
- */
-/*
- * Created on 11/08/2003
- *
- */
-package com.opensymphony.webwork.views.freemarker;
-
-import com.opensymphony.webwork.ServletActionContext;
-import com.opensymphony.webwork.views.jsp.ui.OgnlTool;
-import com.opensymphony.webwork.views.util.UrlHelper;
-
-import com.opensymphony.xwork.util.OgnlValueStack;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-
-/**
- * @author CameronBraid
- *
- */
-public class FreemarkerUtil {
-    //~ Instance fields ////////////////////////////////////////////////////////
-
-    private OgnlTool ognlTool = OgnlTool.getInstance();
-    private OgnlValueStack valueStack;
-
-    //~ Constructors ///////////////////////////////////////////////////////////
-
-    /**
-     *
-     */
-    public FreemarkerUtil() {
-        super();
-        valueStack = ServletActionContext.getContext().getValueStack();
-    }
-
-    //~ Methods ////////////////////////////////////////////////////////////////
-
-    public String buildUrl(String url) {
-        return UrlHelper.buildUrl(url, ServletActionContext.getRequest(), ServletActionContext.getResponse(), null);
-    }
-
-    public Object findValue(String expression) {
-        return valueStack.findValue(expression);
-    }
-
-    public Object findValue(String expression, String className) throws ClassNotFoundException {
-        return valueStack.findValue(expression, Class.forName(className));
-    }
-
-    public Object findValue(String expression, Object obj) throws ClassNotFoundException {
-        return ognlTool.findValue(expression, obj);
-    }
-
-    public List makeSelectList(String list, String listKey, String listValue) {
-        List selectList = new ArrayList();
-        Collection items = (Collection) valueStack.findValue(list);
-
-        if (items != null) {
-            for (Iterator iter = items.iterator(); iter.hasNext();) {
-                Object element = (Object) iter.next();
-                Object key = null;
-
-                if ((listKey == null) || (listKey.length() == 0)) {
-                    key = element;
-                } else {
-                    key = ognlTool.findValue(listKey, element);
-                }
-
-                Object value = null;
-
-                if ((listValue == null) || (listValue.length() == 0)) {
-                    value = element;
-                } else {
-                    value = ognlTool.findValue(listValue, element);
-                }
-
-                selectList.add(new ListEntry(key, value));
-            }
-        }
-
-        return selectList;
-    }
-}

src/java/com/opensymphony/webwork/views/freemarker/ListEntry.java

-/*
- * Copyright (c) 2002-2003 by OpenSymphony
- * All rights reserved.
- */
-/*
- * Created on 6/09/2003
- *
- */
-package com.opensymphony.webwork.views.freemarker;
-
-
-/**
- * @author CameronBraid
- *
- */
-public class ListEntry {
-    //~ Instance fields ////////////////////////////////////////////////////////
-
-    final private Object key;
-    final private Object value;
-
-    //~ Constructors ///////////////////////////////////////////////////////////
-
-    public ListEntry(Object key, Object value) {
-        this.key = key;
-        this.value = value;
-    }
-
-    //~ Methods ////////////////////////////////////////////////////////////////
-
-    public Object getKey() {
-        return key;
-    }
-
-    public Object getValue() {
-        return value;
-    }
-}

src/java/com/opensymphony/webwork/views/freemarker/ScopesHashModel.java

+package com.opensymphony.webwork.views.freemarker;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import freemarker.template.ObjectWrapper;
+import freemarker.template.SimpleHash;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ * Simple Hash model that also searches other scopes.
+ * 
+ * If the key doesn't exist in this hash, this template model tries to 
+ * resolve the key within the attributes of the following scopes, 
+ * in the order stated: Request, Session, Servlet Context
+ *   
+ */
+public class ScopesHashModel extends SimpleHash
+{
+    private ObjectWrapper objectWraper;
+    private ServletContext servletContext;
+    private HttpServletRequest request;
+    
+    public ScopesHashModel(ObjectWrapper objectWrapper, ServletContext context, HttpServletRequest request) {
+        super();
+        this.objectWraper = objectWrapper;
+        this.servletContext = context;
+        this.request = request;
+    }
+
+
+    public TemplateModel get(String key) throws TemplateModelException {
+
+        // Lookup in default scope
+        TemplateModel model = super.get(key);
+        if(model != null) {
+            return model;
+        }
+        
+        
+        if (request != null) {
+	        // Lookup in request scope
+	        Object obj = request.getAttribute(key);
+	        if(obj != null) {
+	            return objectWraper.wrap(obj);
+	        }
+
+	        // Lookup in session scope
+	        HttpSession session = request.getSession(false);
+	        if(session != null) {
+	            obj = session.getAttribute(key);
+	            if(obj != null) {
+	                return objectWraper.wrap(obj);
+	            }
+	        }
+        }
+        
+        if (servletContext != null) {
+	        // Lookup in application scope
+	        Object obj = servletContext.getAttribute(key);
+	        if(obj != null) {
+	            return objectWraper.wrap(obj);
+	        }
+        }
+        
+        return null;
+    }
+}

src/java/com/opensymphony/webwork/views/freemarker/ValueStackModel.java

  */
 package com.opensymphony.webwork.views.freemarker;
 
-import com.opensymphony.xwork.ActionContext;
 import com.opensymphony.xwork.util.OgnlValueStack;
 
+import freemarker.template.ObjectWrapper;
 import freemarker.template.SimpleHash;
 import freemarker.template.TemplateHashModel;
 import freemarker.template.TemplateModel;
 /**
  * @author CameronBraid
  */
-public class ValueStackModel extends SimpleHash implements TemplateHashModel {
+public class ValueStackModel extends SimpleHash {
+
     //~ Instance fields ////////////////////////////////////////////////////////
 
+    private TemplateHashModel wrappedModel= null;
+    
     protected OgnlValueStack stack = null;
-    protected TemplateHashModel wrappedModel = null;
 
     //~ Constructors ///////////////////////////////////////////////////////////
 
     /**
      *
      */
-    public ValueStackModel(TemplateHashModel wrappedModel) {
+    public ValueStackModel(OgnlValueStack stack, TemplateHashModel wrappedModel, ObjectWrapper wrapper) {
         super();
+        this.stack = stack;
         this.wrappedModel = wrappedModel;
-
-        if (ActionContext.getContext() != null) {
-            this.stack = ActionContext.getContext().getValueStack();
-        }
+        this.setObjectWrapper(wrapper);
     }
 
     //~ Methods ////////////////////////////////////////////////////////////////
     /* (non-Javadoc)
     * @see freemarker.template.TemplateHashModel#isEmpty()
     */
-    public boolean isEmpty() throws TemplateModelException {
+    public boolean isEmpty() {
         // not quite sure what to return here.
         // should we check if anything exists in the stack ? - though won't something always exist ?
         return false;
             }
         }
 
-        return wrappedModel.get(key);
+        if (wrappedModel ==  null) {
+            return wrap(null);
+        } else {
+            return wrappedModel.get(key);
+        }
     }
 }

src/java/com/opensymphony/webwork/views/jsp/ui/AbstractUITag.java

 import org.apache.velocity.app.VelocityEngine;
 import org.apache.velocity.context.Context;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.servlet.jsp.JspException;
 import java.io.Writer;
 import java.util.HashMap;
 
     protected void mergeTemplate(String templateName) throws Exception {
         Template t = velocityEngine.getTemplate(templateName);
-        Context context = velocityManager.createContext(getStack(), pageContext.getRequest(), pageContext.getResponse());
+        Context context = velocityManager.createContext(getStack(), (HttpServletRequest)pageContext.getRequest(), (HttpServletResponse)pageContext.getResponse());
 
         Writer outputWriter = pageContext.getOut();
 

src/java/com/opensymphony/webwork/views/velocity/VelocityManager.java

  */
 package com.opensymphony.webwork.views.velocity;
 
-import com.opensymphony.webwork.config.Configuration;
-import com.opensymphony.webwork.util.WebWorkUtil;
-import com.opensymphony.webwork.views.jsp.ui.OgnlTool;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
 
-import com.opensymphony.xwork.ActionContext;
-import com.opensymphony.xwork.ActionInvocation;
-import com.opensymphony.xwork.ObjectFactory;
-import com.opensymphony.xwork.util.OgnlValueStack;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.Velocity;
 import org.apache.velocity.app.VelocityEngine;
 import org.apache.velocity.context.Context;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import java.util.*;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
+import com.opensymphony.webwork.config.Configuration;
+import com.opensymphony.webwork.util.VelocityWebWorkUtil;
+import com.opensymphony.webwork.util.WebWorkUtil;
+import com.opensymphony.webwork.views.jsp.ui.OgnlTool;
+import com.opensymphony.xwork.ActionContext;
+import com.opensymphony.xwork.ActionInvocation;
+import com.opensymphony.xwork.ObjectFactory;
+import com.opensymphony.xwork.util.OgnlValueStack;
 
 
 /**
 *
 * @return a new WebWorkVelocityContext
 */
-    public Context createContext(OgnlValueStack stack, ServletRequest servletRequest, ServletResponse servletResponse) {
+    public Context createContext(OgnlValueStack stack, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
         WebWorkVelocityContext context = new WebWorkVelocityContext(chainedContexts, stack);
         context.put(REQUEST, servletRequest);
         context.put(RESPONSE, servletResponse);
         context.put(STACK, stack);
         context.put(OGNL, ognlTool);
-        context.put(WEBWORK, new WebWorkUtil(context, stack));
+        context.put(WEBWORK, new VelocityWebWorkUtil(context, stack, servletRequest, servletResponse));
 
         ActionInvocation invocation = (ActionInvocation) stack.getContext().get(ActionContext.ACTION_INVOCATION);
 

src/java/webwork-default.xml

             <result-type name="chain" class="com.opensymphony.xwork.ActionChainResult"/>
             <result-type name="xslt" class="com.opensymphony.webwork.views.xslt.XSLTResult"/>
             <result-type name="jasper" class="com.opensymphony.webwork.views.jasperreports.JasperReportsResult"/>
+			<result-type name="freemarker" class="com.opensymphony.webwork.views.freemarker.FreemarkerResult"/>
         </result-types>
 
         <interceptors>

src/test/com/opensymphony/webwork/views/freemarker/FreemarkerTest.java

  */
 package com.opensymphony.webwork.views.freemarker;
 
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import com.opensymphony.webwork.util.FreemarkerWebWorkUtil;
+import com.opensymphony.webwork.util.ListEntry;
 import com.opensymphony.xwork.ActionContext;
 import com.opensymphony.xwork.util.OgnlValueStack;
 
+import freemarker.template.ObjectWrapper;
 import freemarker.template.SimpleHash;
 import freemarker.template.SimpleSequence;
 
-import junit.framework.TestCase;
-
-import java.util.Collection;
-import java.util.List;
-
 
 /**
  * @author CameronBraid
     //~ Methods ////////////////////////////////////////////////////////////////
 
     public void testSelectHelper() {
-        FreemarkerUtil wwUtil = new FreemarkerUtil();
+        FreemarkerWebWorkUtil wwUtil = new FreemarkerWebWorkUtil(ActionContext.getContext().getValueStack(), null, null);
 
         List selectList = null;
 
-        selectList = wwUtil.makeSelectList("stringList", null, null);
+        selectList = wwUtil.makeSelectList("ignored", "stringList", null, null);
         assertEquals("one", ((ListEntry) selectList.get(0)).getKey());
         assertEquals("one", ((ListEntry) selectList.get(0)).getValue());
 
-        selectList = wwUtil.makeSelectList("beanList", "name", "value");
+        selectList = wwUtil.makeSelectList("ignored", "beanList", "name", "value");
         assertEquals("one", ((ListEntry) selectList.get(0)).getKey());
         assertEquals("1", ((ListEntry) selectList.get(0)).getValue());
     }
 
     public void testValueStackMode() throws Exception {
-        ValueStackModel model = new ValueStackModel(new SimpleHash());
+        ValueStackModel model = new ValueStackModel(ActionContext.getContext().getValueStack(), new SimpleHash(), ObjectWrapper.DEFAULT_WRAPPER);
 
         SimpleSequence stringList = null;
 

src/test/com/opensymphony/webwork/views/velocity/TagDirectiveTest.java

     public void testBodyTag() throws Exception {
         Template template = velocityEngine.getTemplate("/com/opensymphony/webwork/views/velocity/bodytag.vm");
         StringWriter writer = new StringWriter();
-        Context context = VelocityManager.getInstance().createContext(stack, (ServletRequest) mockRequest.proxy(), (ServletResponse) mockResponse.proxy());
+        Context context = VelocityManager.getInstance().createContext(stack, (HttpServletRequest) mockRequest.proxy(), (HttpServletResponse) mockResponse.proxy());
         template.merge(context, writer);
 
         // verify that we got one param, hello=world
     public void testTag() throws Exception {
         Template template = velocityEngine.getTemplate("/com/opensymphony/webwork/views/velocity/tag.vm");
         StringWriter writer = new StringWriter();
-        Context context = VelocityManager.getInstance().createContext(stack, (ServletRequest) mockRequest.proxy(), (ServletResponse) mockResponse.proxy());
+        Context context = VelocityManager.getInstance().createContext(stack, (HttpServletRequest) mockRequest.proxy(), (HttpServletResponse) mockResponse.proxy());
         template.merge(context, writer);
 
         // verify that our date thingy was populated correctly

src/webapp/WEB-INF/web.xml

     <servlet>
         <servlet-name>freemarker</servlet-name>
         <servlet-class>com.opensymphony.webwork.views.freemarker.FreemarkerServlet</servlet-class>
-
-      <!-- FreemarkerServlet settings: -->
-      <init-param>
-        <param-name>TemplatePath</param-name>
-        <param-value>/</param-value>
-      </init-param>
-      <init-param>
-        <param-name>NoCache</param-name>
-        <param-value>true</param-value>
-      </init-param>
-      <init-param>
-        <param-name>ContentType</param-name>
-        <param-value>text/html</param-value>
-      </init-param>
-        
-      <!-- FreeMarker settings: -->
-      <init-param>
-        <param-name>template_update_delay</param-name>
-        <param-value>0</param-value> <!-- 0 is for development only! Use higher value otherwise. -->
-      </init-param>
-      <init-param>
-        <param-name>default_encoding</param-name>
-        <param-value>ISO-8859-1</param-value>
-      </init-param>
-      <init-param>
-        <param-name>number_format</param-name>
-        <param-value>0.##########</param-value>
-      </init-param>
-    
-      <load-on-startup>1</load-on-startup>
+		<load-on-startup>1</load-on-startup>
     </servlet>
 
     <servlet>

src/webapp/country.ftl

-<#assign benchmark=JspTaglibs["/WEB-INF/taglibs-benchmark.tld"] />
-<#assign webwork=JspTaglibs["/WEB-INF/webwork.tld"] />
+<#--
 
-<#assign count = 100/>
+Freemarker Demonstration
+-------------------------
 
-<h2>generate output below ${count} times</h2>
-<@benchmark.duration >
-	<@repeat macro=output count=count/>
-</@benchmark.duration>
+* uses the benchmark taglib to measure the speed of generating an option list
+* illustrates iteration over a list
+* illustrates using a macro as a parameter... do that in velocity :)
+* illustrates scraping the output of a nested template block into a variable
+* illustrates string to number conversion, and vice versa
+
+-->
+<#assign benchmark=JspTaglibs["/WEB-INF/taglibs-benchmark.tld"] />
+<#assign count = 100/>
+<html>
+<head>
+	<title>Freemarker Demo</title>
+	<link rel="stylesheet" href="styles.css">
+</head>
+<body>
+<@heading>Freemarker Demonstration</@heading>
+<ul>
+<li>uses the benchmark taglib to measure the speed of generating an option list
+<li>illustrates iteration over a list
+<li>illustrates using a macro as a parameter... do that in velocity :)
+<li>illustrates scraping the output of a nested template block into a variable
+<li>illustrates string to number conversion, and vice versa
+</ul>
+<@title>Duration to output country list</@title>
+<@msg>(averaged over ${count} repetitions)</@msg>
+<p>
+	<#assign duration><@benchmark.duration><@repeat macro=renderCountries count=count/></@benchmark.duration></#assign>
+	${duration?number / count} ms each
+</p>
+<@title>Country List</@title>
+<@renderCountries/>
+</body>
+</html>
 
-<hr>
-<@output/>
+<#macro heading>
+	<h1><#nested/></h1>
+</#macro>
+<#macro msg>
+	<p class="msg"><#nested/></p>
+</#macro>
+<#macro title>
+	<p class="title"><#nested/></p>
+</#macro>
 
-<#macro output count=1>
+<#macro renderCountries>
 	<ul>
 		<#list countries as country>
 			<li>${country[0]}</li>

src/webapp/default.jsp

 <html>
 <head>
     <title>WebWork2 Examples</title>
-    <style type="text/css">
-    BODY { font-size : 100%; }
-    BODY, TD, TH, DIV, P {
-        font-family : verdana, arial, helvetica, sans-serif;
-        font-size : 9pt;
-    }
-    H1 {
-        font-size : 14pt;
-        border-bottom : 1px #ccc solid;
-    }
-    TABLE {
-        border : 1px #ccc solid;
-    }
-    TH {
-        text-align : left;
-        background-color : #ddd;
-    }
-    UL {
-        padding-bottom : 0.5em;
-    }
-    LI {
-        margin-bottom : 0.5em;
-    }
-    .title {
-        font-weight : bold;
-        font-size : 11pt;
-    }
-    .msg {
-        color : #f00;
-    }
-    </style>
+    <link rel="stylesheet" href="styles.css">
 </head>
 
 <body>
     
     <li><a href="indexedProperties.action">Indexed property example</a></li>
 </ul>
+    
+<p class="title">
+Freemarker Demos
+</p>
+<ul>
+    <li><a href="FreemarkerTest.action">FreeMarker Example</a></li>
+    <li><a href="FreemarkerTest_Servlet.action">FreeMarker Example (using servlet)</a></li>
+</ul>
 
 <br><br>
 
 </body>
-</html>
+</html>

src/webapp/styles.css

+BODY { font-size : 100%; }
+BODY, TD, TH, DIV, P {
+    font-family : verdana, arial, helvetica, sans-serif;
+    font-size : 9pt;
+}
+H1 {
+    font-size : 14pt;
+    border-bottom : 1px #ccc solid;
+}
+TABLE {
+    border : 1px #ccc solid;
+}
+TH {
+    text-align : left;
+    background-color : #ddd;
+}
+UL {
+    padding-bottom : 0.5em;
+}
+LI {
+    margin-bottom : 0.5em;
+}
+.title {
+    font-weight : bold;
+    font-size : 11pt;
+}
+.msg {
+    color : #f00;
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.