package dark.web.frame.velocity.form;
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.Message;
import dark.web.frame.Value;
import dark.web.frame.form.AbstractForm;
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: VelocityForm
* Description: 当表单验证失败时,将向Velocity模版写入两个变量,
* Message和Value,分别是消息对象和值对象
* Copyright: Copyright (c) 2004
* Company: DIS
* Create Time: 2005-2-28 15:41:36
* @author darkhe
* @version 1.0
*/
public abstract class VelocityForm extends AbstractForm
{
/** 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;
/**
* default通过验证,返回true,失败返回false
*/
private boolean isPass = false;
/**
* 表单验证有错,返回input模板,否则返回null
* @param request
* @param response
* @param context
* @return
* @throws Exception
*/
public Template validate(
HttpServletRequest request,
HttpServletResponse response,
Context context)
throws Exception
{
Message m = validate(context);
//log.debug("m.isEmpty()=" + m.isEmpty());
// m不为空则说明有表单验证有错,返回input模板,否则返回null
if (!m.isEmpty())
{
//log.debug("m不为空则说明有表单验证有错,返回input模板,否则返回null");
context.put("Message", m);
context.put("Value", loadValue(request));
Template t = getTemplate(getInput());
return t;
}
else
{
//log.debug("m不为空则说明有表单验证有错,返回input模板,否则返回null");
return null;
}
}
/**
* 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("VelocityViewServlet : 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("VelocityViewServlet: 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("VelocityViewServlet: " +
"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;
}
/**
* @throws ServletException
* @throws IOException
* @see dark.web.frame.Form#process()
*/
public void validate(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
Context context = null;
try
{
/*
* first, get a context
*/
context = createContext(request, response);
/*
* set the content type
*/
response.setContentType(contentType);
/*
* let someone handle the request
*/
Template template = validate(request, response, context);
/*
* bail if we can't find the template
*/
if (template == null)
{
setPass(true);
//log.info("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);
}
}
/**
* @return 表单通过验证,返回true,失败返回false
*/
public boolean isPass()
{
return isPass;
}
/**
* @param b
*/
public void setPass(boolean b)
{
isPass = b;
}
/**
* @return
* @throws ServletException
* @throws IOException
* @see dark.web.frame.Form#validate()
*/
public Message validate(Context context) throws ServletException, IOException
{
HttpServletRequest request = getRequest();
Message m = getMessage();
Value v = loadValue(request);
validate(request, m, v,context);
saveMessage(m, request);
saveValue(v, request);
return m;
}
/**
* 子类通过实现当前方法现实现验证逻辑
* @param request
* @param m 消息对象
* @param v 值对象
* @throws ServletException
* @throws IOException
*/
public abstract void validate(
HttpServletRequest request,
Message m,
Value v,
Context context)
throws ServletException, IOException;
/**
* @return
* @throws ServletException
* @throws IOException
* @see dark.web.frame.Form#validate()
*/
public Message validate() throws ServletException, IOException
{
throw new ServletException("Velocity is not need validate()");
}
/**
* @return
*/
public String getContentType()
{
return contentType;
}
/**
* @return
*/
public String getOutputEncoding()
{
return outputEncoding;
}
/**
* @return
*/
public ToolboxManager getToolboxManager()
{
return toolboxManager;
}
/**
* @param string
*/
public void setContentType(String string)
{
contentType = string;
}
/**
* @param string
*/
public void setOutputEncoding(String string)
{
outputEncoding = string;
}
/**
* @param manager
*/
public void setToolboxManager(ToolboxManager manager)
{
toolboxManager = manager;
}
}