package dark.web.frame.velocity.page;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import dark.web.frame.Value;
import dark.web.frame.page.AbstractPage;
import org.apache.velocity.Template;
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 org.apache.velocity.io.VelocityWriter;
import org.apache.velocity.runtime.RuntimeSingleton;
import org.apache.velocity.tools.view.ToolboxManager;
import org.apache.velocity.tools.view.context.ChainedContext;
import org.apache.velocity.util.SimplePool;
/**
* Title: VelocityPage
* Description: 支持Velocity的Page处理超类
* 根据org.apache.velocity.servlet.VelocityServlet进行修改,
* 从而将Velocity整合进dwf框架
* Copyright: Copyright (c) 2005
* Company: DIS
* Create Time: 2005-3-21 15:40:34
* @author darkhe
* @version 1.0
*/
public abstract class VelocityPage extends AbstractPage
{
/** Cache of writers */
private static SimplePool writerPool = new SimplePool(40);
/** A reference to the toolbox manager. */
protected ToolboxManager toolboxManager = null;
private String contentType = "text/html";
/** encoding for the output stream */
private String outputEncoding = "ISO-8859-1";
/**
* Whether we've logged a deprecation warning for
* ServletResponse's getOutputStream().
* @since VelocityTools 1.1
*/
private boolean warnOfOutputStreamDeprecation = true;
public Template process(
HttpServletRequest request,
HttpServletResponse response,
Context ctx)
throws Exception
{
Value v = loadValue(request);
Template t = process(request, response, ctx, v);
saveValue(v, request);
ctx.put("Value", v);
ctx.put("Request", request);
ctx.put("Response", response);
ctx.put("Session", request.getSession());
return t;
}
/**
* 子类必须实现当前方法以实现业务逻辑
* @param request
* @param response
* @param context
* @param v
* @return
* @throws Exception
*/
public abstract Template process(
HttpServletRequest request,
HttpServletResponse response,
Context context,
Value v)
throws Exception;
/**
* @throws ServletException
* @throws IOException
* @see dark.web.frame.Command#process()
*/
public void process() throws ServletException, IOException
{
Context context = null;
HttpServletRequest request = getRequest();
HttpServletResponse response = getResponse();
try
{
/*
* first, get a context
*/
context = createContext(request, response);
/*
* set the content type
*/
response.setContentType(contentType);
/*
* let someone handle the request
*/
Template template = process(request, response, context);
/*
* bail if we can't find the template
*/
if (template == null)
{
log.warn("template is null");
return;
}
/*
* now merge it
*/
mergeTemplate(template, context, response);
}
catch (Exception e)
{
/*
* call the error handler to let the derived class
* do something useful with this failure.
*/
error(request, response, e);
}
finally
{
/*
* call cleanup routine to let a derived class do some cleanup
*/
requestCleanup(request, response, context);
}
}
/**
* Cleanup routine called at the end of the request processing sequence
* allows a derived class to do resource cleanup or other end of
* process cycle tasks. This default implementation does nothing.
*
* @param request servlet request from client
* @param response servlet reponse
* @param context Context created by the {@link #createContext}
*/
protected void requestCleanup(HttpServletRequest request,
HttpServletResponse response,
Context context)
{
}
/**
* Handle the template processing request.
*
* @param request client request
* @param response client response
* @param ctx VelocityContext to fill
*
* @return Velocity Template object or null
*/
protected Template handleRequest(HttpServletRequest request,
HttpServletResponse response,
Context ctx)
throws Exception
{
// If we get here from RequestDispatcher.include(), getServletPath()
// will return the original (wrong) URI requested. The following special
// attribute holds the correct path. See section 8.3 of the Servlet
// 2.3 specification.
String path = (String)request.getAttribute("javax.servlet.include.servlet_path");
if (path == null)
{
path = request.getServletPath();
}
return getTemplate(path);
}
/**
* Creates and returns an initialized Velocity context.
*
* A new context of class {@link ChainedContext} is created and
* initialized.
*
* @param request servlet request from client
* @param response servlet reponse to client
*/
protected Context createContext(HttpServletRequest request,
HttpServletResponse response)
{
ChainedContext ctx = new ChainedContext(null, request, response, getServletContext());
/* if we have a toolbox manager, get a toolbox from it */
if (toolboxManager != null)
{
ctx.setToolbox(toolboxManager.getToolboxContext(ctx));
}
return ctx;
}
/**
* Retrieves the requested template.
*
* @param name The file name of the template to retrieve relative to the
* template root.
* @return The requested template.
* @throws ResourceNotFoundException if template not found
* from any available source.
* @throws ParseErrorException if template cannot be parsed due
* to syntax (or other) error.
* @throws Exception if an error occurs in template initialization
*/
public Template getTemplate(String name)
throws ResourceNotFoundException, ParseErrorException, Exception
{
return RuntimeSingleton.getTemplate(name);
}
/**
* Retrieves the requested template with the specified character encoding.
*
* @param name The file name of the template to retrieve relative to the
* template root.
* @param encoding the character encoding of the template
* @return The requested template.
* @throws ResourceNotFoundException if template not found
* from any available source.
* @throws ParseErrorException if template cannot be parsed due
* to syntax (or other) error.
* @throws Exception if an error occurs in template initialization
*/
public Template getTemplate(String name, String encoding)
throws ResourceNotFoundException, ParseErrorException, Exception
{
return RuntimeSingleton.getTemplate(name, encoding);
}
/**
* Merges the template with the context. Only override this if you really, really
* really need to. (And don't call us with questions if it breaks :)
*
* @param template template object returned by the handleRequest() method
* @param context Context created by the {@link #createContext}
* @param response servlet reponse (used to get a Writer)
*/
protected void mergeTemplate(Template template,
Context context,
HttpServletResponse response)
throws ResourceNotFoundException, ParseErrorException,
MethodInvocationException, IOException,
UnsupportedEncodingException, Exception
{
VelocityWriter vw = null;
Writer writer = getResponseWriter(response);
try
{
vw = (VelocityWriter)writerPool.get();
if (vw == null)
{
vw = new VelocityWriter(writer, 4 * 1024, true);
}
else
{
vw.recycle(writer);
}
template.merge(context, vw);
}
finally
{
if (vw != null)
{
try
{
// flush and put back into the pool
// don't close to allow us to play
// nicely with others.
vw.flush();
/* This hack sets the VelocityWriter's internal ref to the
* PrintWriter to null to keep memory free while
* the writer is pooled. See bug report #18951 */
vw.recycle(null);
writerPool.put(vw);
}
catch (Exception e)
{
Velocity.debug("VelocityViewServlet: " +
"Trouble releasing VelocityWriter: " +
e.getMessage());
}
}
}
}
/**
* Invoked when there is an error thrown in any part of doRequest() processing.
*
* Default will send a simple HTML response indicating there was a problem.
*
* @param request original HttpServletRequest from servlet container.
* @param response HttpServletResponse object from servlet container.
* @param e Exception that was thrown by some other part of process.
*/
protected void error(HttpServletRequest request,
HttpServletResponse response,
Exception e)
throws ServletException
{
try
{
StringBuffer html = new StringBuffer();
html.append("\n");
html.append("Error\n");
html.append("\n");
html.append("VelocityPage : Error processing the template\n");
Throwable cause = e;
String why = cause.getMessage();
if (why != null && why.trim().length() > 0)
{
html.append(why);
html.append("\n\n");
}
// if it's an MIE, i want the real stack trace!
if (cause instanceof MethodInvocationException)
{
// get the real cause
cause = ((MethodInvocationException)cause).getWrappedThrowable();
}
StringWriter sw = new StringWriter();
cause.printStackTrace(new PrintWriter(sw));
html.append("\n");
html.append(sw.toString());
html.append("\n");
html.append("\n");
html.append("");
getResponseWriter(response).write(html.toString());
}
catch (Exception e2)
{
// clearly something is quite wrong.
// let's log the new exception then give up and
// throw a servlet exception that wraps the first one
Velocity.error("VelocityPage: Exception while printing error screen: "+e2);
throw new ServletException(e);
}
}
/**
* Procure a Writer with correct encoding which can be used
* even if HttpServletResponse's getOutputStream() method
* has already been called.
*
* This is a transitional method which will be removed in a
* future version of Velocity. It is not recommended that you
* override this method.
*
* @param response The response.
* @return A Writer, possibly created using the
* getOutputStream().
*/
protected Writer getResponseWriter(HttpServletResponse response)
throws UnsupportedEncodingException, IOException
{
Writer writer = null;
try
{
writer = response.getWriter();
}
catch (IllegalStateException e)
{
// ASSUMPTION: We already called getOutputStream(), so
// calls to getWriter() fail. Use of OutputStreamWriter
// assures our desired character set
if (this.warnOfOutputStreamDeprecation)
{
this.warnOfOutputStreamDeprecation = false;
Velocity.warn("VelocityPage: " +
"Use of ServletResponse's getOutputStream() " +
"method with VelocityViewServlet is " +
"deprecated -- support will be removed in " +
"an upcoming release");
}
// Assume the encoding has been set via setContentType().
String encoding = response.getCharacterEncoding();
if (encoding == null)
{
encoding = outputEncoding;
}
writer = new OutputStreamWriter(response.getOutputStream(),
encoding);
}
return writer;
}
/**
* @return
*/
public String getContentType()
{
return contentType;
}
/**
* @param string
*/
public void setContentType(String string)
{
contentType = string;
}
/**
* @return
*/
public ToolboxManager getToolboxManager()
{
return toolboxManager;
}
/**
* @param manager
*/
public void setToolboxManager(ToolboxManager manager)
{
toolboxManager = manager;
}
/**
* @return
*/
public String getOutputEncoding()
{
return outputEncoding;
}
/**
* @param string
*/
public void setOutputEncoding(String string)
{
outputEncoding = string;
}
}