/*
* Copyright (C) 2004 TiongHiang Lee
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Email: thlee@onemindsoft.org
*/
package org.onemind.swingweb.servlet;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import org.apache.commons.fileupload.DiskFileUpload;
import org.onemind.awtbridge.BridgeContextSession;
import org.onemind.awtbridge.session.SessionLifecycleListener;
import org.onemind.commons.java.util.*;
import org.onemind.commons.java.xml.digest.SaxDigesterHandler;
import org.onemind.jxp.*;
import org.onemind.swingweb.*;
import org.onemind.swingweb.resource.Resource;
import org.onemind.swingweb.session.SwingWebSession;
import org.onemind.swingweb.session.URLLocal;
import org.onemind.swingweb.util.ComponentOptions;
/**
* A Abstract Servlet for initializing the swingweb application. There's only two parameter needed
* for the application: <br>
* <ul>
* <li>app-class - the application class </li>
* <li>swingweb-config - the swing web configuration </li>
* </ul>
*
* @author TiongHiang Lee (thlee@onemindsoft.org)
*
*/
public abstract class AbstractSwingWebServlet extends HttpServlet implements SessionLifecycleListener
{
/** the logger * */
private static final Logger _logger = Logger.getLogger(AbstractSwingWebServlet.class.getName());
public static final String KEY_SWCONTEXTS = "SwingWebContexts";
private static final String KEY_APP_CLASS = "app-class";
private static final String KEY_CONFIG = "swingweb-config";
private static final String KEY_ENCODING = "char-encoding";
private static final String KEY_GET_AFTER_POST = "get-after-post";
private static final String KEY_TEMPLATE_CONTENT = "content-template";
private static final String KEY_TEMPLATE_SESSION_ENDED = "session-ended-template";
private static final String KEY_COMPONENT_OPTIONS = "component-options-config";
/** the application to start **/
private String _appClass;
/** the session ended message **/
private String _sessionEndedMessage;
/** redirect to get after post **/
private boolean _getAfterPost = true;
/** the upload **/
private DiskFileUpload _uploadRepository;
/** the swing web manager **/
private SwingWebAppManager _manager;
/** the encoding **/
private String _charEncoding;
/** the sessions **/
private WeakHashMap _sessions = new WeakHashMap();
/** the jxp processor **/
private JxpProcessor _processor;
/** the context template **/
private String _contentTemplate;
/** the session end template **/
private String _sessionEndTemplate;
/** the component options **/
private ComponentOptions _comOpts;
/** the component options file **/
private File _comOptsFile;
/** a callback to pass to the template for rendering **/
public interface CallBack
{
public void call() throws Exception;
}
class RenderCallBack implements CallBack
{
Writer _writer;
public RenderCallBack(Writer writer)
{
_writer = writer;
}
public void call() throws Exception
{
_manager.renderOutput(_writer);
}
};
class InputRenderCallBack extends RenderCallBack
{
Map _form;
public InputRenderCallBack(Writer writer, Map form)
{
super(writer);
_form = form;
}
public void call() throws Exception
{
_manager.handleInput(_form);
_manager.renderOutput(_writer);
}
};
/**
* {@inheritDoc}
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
doPost(req, resp);
}
public String getConfig(ServletConfig config, String key, String def)
{
String value = config.getInitParameter(key);
if (value == null)
{
value = def;
}
return value;
}
/**
* {@inheritDoc}
*/
public void init(ServletConfig config) throws ServletException
{
super.init(config);
//make it run swingweb
System.getProperties().put("awt.toolkit", "org.onemind.swingweb.SwingWebToolkit");
LogUtils.initLoggingFromClassPath();
_appClass = config.getInitParameter(KEY_APP_CLASS);
String swConfig = config.getInitParameter(KEY_CONFIG);
_charEncoding = getConfig(config, KEY_ENCODING, "UTF-8");
_getAfterPost = ObjectUtils.toBool(config.getInitParameter(KEY_GET_AFTER_POST), true);
// create a new file upload handler
_uploadRepository = new DiskFileUpload();
// set upload parameters
_uploadRepository.setRepositoryPath(System.getProperty("java.io.tmpdir")); //TODO: make become configurable setting
//_uploadRepository.setSizeThreshold(1000000); //file > 1 meg write to disk
_uploadRepository.setSizeThreshold(0); //write any file to disk
_uploadRepository.setSizeMax(-1); //no maximum size
_comOpts = loadComponentOptions(config);
try
{
_manager = new SwingWebAppManager(new FileInputStream(getServletContext().getRealPath(swConfig)));
synchronized (KEY_SWCONTEXTS)
{
Set contextSet = (Set) getServletContext().getAttribute(KEY_SWCONTEXTS);
if (contextSet == null)
{
contextSet = new HashSet();
getServletContext().setAttribute(KEY_SWCONTEXTS, contextSet);
}
contextSet.add(_manager.getContext());
}
_contentTemplate = getConfig(config, KEY_TEMPLATE_CONTENT, "/org/onemind/swingweb/servlet/swingweb-app-content.jxp");
_sessionEndTemplate = getConfig(config, KEY_TEMPLATE_SESSION_ENDED,
"/org/onemind/swingweb/servlet/swingweb-app-session-ended.jxp");
} catch (Exception e)
{
throw new ServletException("Cannot init swingweb servlet", e);
}
}
private ComponentOptions loadComponentOptions(ServletConfig config) throws ServletException
{
String configFile = config.getInitParameter(KEY_COMPONENT_OPTIONS);
if (configFile == null)
{
throw new ServletException(KEY_COMPONENT_OPTIONS + " must be configured");
}
File comOptsFile = new File(getServletContext().getRealPath(configFile));
if (comOptsFile.exists())
{
try
{
SaxDigesterHandler handler = new SaxDigesterHandler();
ComponentOptions comOpts = new ComponentOptions();
handler.addDigester(comOpts);
handler.parse(new FileInputStream(comOptsFile));
_comOptsFile = comOptsFile;
return comOpts;
} catch (Exception ex)
{
throw new ServletException("Unable to initialize " + KEY_COMPONENT_OPTIONS + comOptsFile.getAbsolutePath(), ex);
}
} else
{
throw new ServletException(KEY_COMPONENT_OPTIONS + comOptsFile.getAbsolutePath() + " does not exist");
}
}
/**
* Get the processor
* @param req the request
* @return the processor
* @throws Exception
*/
private JxpProcessor getProcessor(HttpServletRequest req)
{
if (_processor == null)
{
synchronized (this)
{
MultiSourcePageSource multisource = new MultiSourcePageSource();
FilePageSource filesource = new FilePageSource(req.getRealPath("/"), _charEncoding);
filesource.setModCheck(true);
filesource.setCaching(true);
multisource.addPageSource(filesource);
ResourceStreamPageSource streamsource = new ResourceStreamPageSource("/");
multisource.addPageSource(streamsource);
_processor = new JxpProcessor(new JxpContext(multisource));
}
}
return _processor;
}
/**
* {@inheritDoc}
*/
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/html; charset=" + _charEncoding);
resp.setStatus(200, "OK");
if (_appClass == null)
{
PrintWriter writer = resp.getWriter();
writer.write("No application is configured at this url");
writer.flush();
writer.close();
} else
{
//TODO: decide whether this environment need to be passed on
Map env = new HashMap();
env.putAll(ServletUtils.getServletEnvironment(getServletConfig()));
env.putAll(ServletUtils.getRequestEnvironment(req));
env.putAll(ServletUtils.getExtraRequestEnvironment(req));
PrintWriter writer = null;
try
{
Map form = ServletUtils.getRequestParameters(req, _uploadRepository);
if (form.containsKey("swresource"))
{
sendResource(req, resp, (String) form.get("swresource"));
} else
{
String uri = req.getRequestURI();
HttpSession session = req.getSession(true);
writer = resp.getWriter();
Object obj = session.getValue(uri);
if (obj instanceof Boolean)
{//session ended
renderWithTemplate(req, null, _sessionEndTemplate, form, writer, null);
session.removeAttribute(uri);
} else
{
try
{
SwingWebSession swSession = (SwingWebSession) obj;
if (swSession == null)
{
swSession = (SwingWebSession) createSession(_manager.getContext(), session);
swSession.addListener(this);
swSession.putValue("SWINGWEB_SESSION_ID", String.valueOf(System.currentTimeMillis()));
session.setAttribute(uri, swSession);
swSession.putAllValues(env);
swSession.putValue("HTTP_FORM", form);
swSession.putValue("HTTP_SESSION", session);
swSession.putValue("COMPONENT_OPTIONS_CONFIG", _comOptsFile.getAbsolutePath());
swSession.putValue("COMPONENT_OPTIONS", _comOpts);
_sessions.put(swSession, session);
_manager.setCurrentSession(swSession);
startApp(swSession, req, resp);
} else
{
swSession.putAllValues(env);
swSession.putValue("COMPONENT_OPTIONS_CONFIG", _comOptsFile.getAbsolutePath());
swSession.putValue("COMPONENT_OPTIONS", _comOpts);
swSession.putValue("HTTP_FORM", form);
swSession.putValue("HTTP_SESSION", session);
_manager.setCurrentSession(swSession);
}
if (_getAfterPost)
{
if (req.getMethod().equalsIgnoreCase("GET"))
{
assertURLLocals(swSession, req);
renderWithTemplate(req, swSession, _contentTemplate, form, writer, new RenderCallBack(writer));
} else
{
_manager.handleInput(form);
StringBuffer url = new StringBuffer(uri);
String queryString = constructURLFromURLLocals(swSession, req);
if (queryString.length() > 0)
{
url.append("?");
url.append(queryString);
}
// url.append("&");
// url.append(System.currentTimeMillis());
resp.sendRedirect(url.toString());
}
} else
{
renderWithTemplate(req, swSession, _contentTemplate, form, writer, new InputRenderCallBack(writer,
form));
}
} finally
{
_manager.setCurrentSession(null);
}
}
}
} catch (IOException e)
{
_logger.severe(LogUtils.getTrace(e));
if (writer != null)
{
e.printStackTrace(writer);
}
} catch (Exception e)
{
//e.printStackTrace(writer);
_logger.severe(LogUtils.getTrace(e));
if (writer != null)
{
e.printStackTrace(writer);
}
}
if (writer != null)
{
writer.flush();
}
resp.flushBuffer();
if (_logger.isLoggable(Level.FINEST))
{
_logger.finest("Done handling request");
}
}
}
private void renderWithTemplate(HttpServletRequest req, SwingWebSession swSession, String templateName, Map form,
Writer writer, CallBack callBack) throws Exception
{
JxpProcessor processor = getProcessor(req);
Map env = new HashMap();
env.put("request", req);
env.put("writer", writer);
env.put("form", form);
env.put("callBack", callBack);
env.put("swSession", swSession);
env.put("session", req.getSession());
processor.process(templateName, writer, env);
}
protected void generateEnvironment()
{
}
protected abstract SwingWebSession createSession(SwingWebContext context, HttpSession session);
protected abstract void startApp(SwingWebSession swSession, HttpServletRequest req, HttpServletResponse resp) throws Exception;
private String constructURLFromURLLocals(SwingWebSession swSession, HttpServletRequest req)
{
StringBuffer sb = new StringBuffer();
SwingWebComponentManager man = (SwingWebComponentManager) swSession.getComponentManager();
List urlLocals = man.getURLLocals();
Iterator it = urlLocals.iterator();
boolean isFirst = true;
while (it.hasNext())
{
URLLocal local = (URLLocal) it.next();
String value = local.getSetValue();
if (value != null)
{
if (isFirst)
{
isFirst = false;
} else
{
sb.append("&");
}
sb.append(URLEncoder.encode(local.getName()));
sb.append("=");
sb.append(URLEncoder.encode(value));
} else if (local.isAlwaysShow())
{
if (isFirst)
{
isFirst = false;
} else
{
sb.append("&");
}
sb.append(URLEncoder.encode(local.getName()));
sb.append("=");
sb.append(URLEncoder.encode((String) local.getValue()));
}
}
return sb.toString();
}
private static void assertURLLocals(SwingWebSession swSession, HttpServletRequest req)
{
Map param = req.getParameterMap();
SwingWebComponentManager man = (SwingWebComponentManager) swSession.getComponentManager();
List urlLocals = man.getURLLocals();
Iterator it = urlLocals.iterator();
while (it.hasNext())
{
URLLocal local = (URLLocal) it.next();
Object value = param.get(local.getName());
if (value instanceof String[])
{
value = ((String[]) value)[0];
}
String paramValue = (String) value;
local.setValue(paramValue);
}
}
/**
* Send the resource
* @param resp
* @param object
*/
private void sendResource(HttpServletRequest req, HttpServletResponse resp, String id)
{
Resource res = _manager.getContext().getResource(id);
if (res != null)
{
try
{
resp.setContentType(res.getType());
resp.setStatus(200, "Ok");
OutputStream out = new BufferedOutputStream(resp.getOutputStream());
FileUtils.copyStream(res.getInputStream(), out, 1024);
out.flush();
} catch (Exception e)
{
_logger.throwing(getClass().getName(), "sendResource", e);
}
} else
{
resp.setContentType("text/html");
resp.setStatus(404, "Resource not found");
}
}
public final SwingWebAppManager getManager()
{
return _manager;
}
public final String getAppClass()
{
return _appClass;
}
public void sessionDestroyed(HttpSessionEvent evt)
{
//close up swingweb sessions when httpsession ended
if (_manager != null)
{
HttpSession session = evt.getSession();
Enumeration names = session.getAttributeNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
Object value = session.getAttribute(name);
if (value instanceof SwingWebSession)
{
SwingWebSession swSession = ((SwingWebSession) value);
_manager.setCurrentSession(swSession);
try
{
swSession.close();
} finally
{
_manager.setCurrentSession(null);
}
}
}
}
}
public void sessionEnded(BridgeContextSession session)
{
//This will be called by Session.close()
HttpSession httpSession = (HttpSession) _sessions.remove(session);
if (httpSession != null)
{
Enumeration names = httpSession.getAttributeNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
if (httpSession.getAttribute(name) == session)
{
httpSession.removeAttribute(name);
httpSession.setAttribute(name, Boolean.FALSE);
}
}
}
}
}