/******************************************************************************* * Copyright 2015 xWic group (http://www.xwic.de) * * Licensed under the Apache License, Version 2.0 (the "License"). * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *******************************************************************************/ /* * de.jwic.web.DispatcherServlet * $Id: WebEngine.java,v 1.25 2013/02/23 22:33:31 lordsam Exp $ */ package de.jwic.web; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.TimeZone; 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 org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.json.JSONException; import org.json.JSONWriter; import de.jwic.base.ConfigurationTool; import de.jwic.base.Control; import de.jwic.base.ControlNotFoundException; import de.jwic.base.Dimension; import de.jwic.base.IActionController; import de.jwic.base.IApplicationSetup; import de.jwic.base.IControlContainer; import de.jwic.base.IResourceControl; import de.jwic.base.JWicException; import de.jwic.base.JWicRuntime; import de.jwic.base.MouseEvent; import de.jwic.base.Page; import de.jwic.base.RenderContext; import de.jwic.base.SessionContext; import de.jwic.base.UserAgentInfo; import de.jwic.base.ValueChangedQueue; import de.jwic.renderer.util.JWicTools; import de.jwic.upload.Upload; import de.jwic.upload.UploadFile; /** * Dispatches incoming request to an existing or new JWic session. * * <p>The DispatcherServlet can initialize a log4j system if required. To do so, * place a log4j.properties file somewhere (WEB-INF is a good place) and point * to it using the init-param "log4j-init-file" in the servlet definition.</p> * <p>Sample: * <pre> * <init-param> * <param-name>log4j-init-file</param-name> * <param-value>WEB-INF/log4j.properties</param-value> * </init-param> * </pre> * </p> * * @author Florian Lippisch * @version $Revision: 1.25 $ */ public class WebEngine { /** * */ private static final String RESPONSE_FORMAT_JSON = "JSON"; private final static String PAGE_FILE = "jwic.page"; private final static String PAGE_LAYER_FILE = "jwic_layer.page"; protected final Log log = LogFactory.getLog(getClass()); private VelocityEngine ve = null; private JWicRuntime jRuntime = null; private IAuthenticator authenticator = null; private IApplicationSetupProvider appSetupProvider = null; private String loginPage = null; private IWebEngineListener[] listeners = new IWebEngineListener[0]; private enum EngineEvent { preHandleAction, postHandleAction, preRendering, postRendering, preControlRendering, postControlRendering, preResourceRequest, postResourceRequest } /** * Used to prepare AjaxUpdates. * * @author Developer */ private class Updateable { String htmlCode = null; Map<String, String> javaScript = null; Set<String> requiredStaticJS = null; /** * @param htmlCode */ public Updateable(String htmlCode) { super(); this.htmlCode = htmlCode; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { // for compatibility reasons, this must return the htmlCode. return htmlCode; } } /** * Constructs a new WebEngine. * @param provider * @throws Exception */ public WebEngine(IApplicationSetupProvider provider, String rootDir) throws Exception { this.appSetupProvider = provider; /* * Initialize the VelocityEngine used by this servlet. */ ve = new VelocityEngine(); Properties veprop = new Properties(); try { File fRootDir = new File(rootDir); veprop.load(new FileInputStream(new File(fRootDir, "WEB-INF/jwic/velocity.WebEngine.properties"))); ConfigurationTool.insertRootPath(veprop); } catch (Exception ex) { log.warn("WEB-INF/jwic/velocity.WebEngine.properties not found, using defaults"); veprop.setProperty("resource.loader", "file,class"); veprop.setProperty("file.resource.loader.description", "Velocity File Resource Loader"); veprop.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader"); veprop.setProperty("file.resource.loader.path", rootDir); veprop.setProperty("file.resource.loader.cache", "true"); veprop.setProperty("file.resource.loader.modificationCheckInterval", "2"); veprop.setProperty("class.resource.loader.description", "Velocity Classpath Resource Loader"); veprop.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); } ve.init(veprop); jRuntime = JWicRuntime.getJWicRuntime(); } /** * Add a IWebEngineListener. */ public synchronized void addWebEngineListener(IWebEngineListener listener) { IWebEngineListener[] l2 = new IWebEngineListener[listeners.length + 1]; System.arraycopy(listeners, 0, l2, 0, listeners.length); l2[listeners.length] = listener; listeners = l2; } /** * Add a IWebEngineListener. */ public synchronized void removeWebEngineListener(IWebEngineListener listener) { ArrayList<IWebEngineListener> lTmp = new ArrayList<IWebEngineListener>(); for(IWebEngineListener l : listeners) { if (l != listener) { lTmp.add(l); } } listeners = (IWebEngineListener[]) lTmp.toArray(); } /** * Fire the event. * @param evType * @param event */ private void fireEvent(EngineEvent evType, WebEngineEvent event) { if (listeners.length != 0) { for (IWebEngineListener listener : listeners) { switch (evType) { case preRendering: listener.preRendering(event); break; case postRendering: listener.postRendering(event); break; case preControlRendering: listener.preControlRendering(event); break; case postControlRendering: listener.postControlRendering(event); break; case preHandleAction: listener.preHandleAction(event); break; case postHandleAction: listener.postHandleAction(event); break; case preResourceRequest: listener.preResourceRequest(event); break; case postResourceRequest: listener.postResourceRequest(event); break; } } } } /** * Checks ModuleSession authentication. * @param ms * @param req * @param res * @return true if request is authenticated * @throws NotAuthenticatedException */ private void handleAuthentication(SessionContext sc, HttpServletRequest req, HttpServletResponse res) throws NotAuthenticatedException { if (sc.getProperty(SessionContext.PROP_AUTHENTICATION, "false").equals("true")) { if (authenticator == null || !authenticator.isAuthenticated(req)) { // redirect to login page throw new NotAuthenticatedException(); } } } /** * @param req * @param res */ public void handleRequest(HttpServletRequest req, HttpServletResponse res, Upload upload) { servletContainerFixes(req, res); long start = System.currentTimeMillis(); boolean resourceMode = "1".equals(req.getParameter(IResourceControl.URL_RESOURCE_PARAM)); boolean ajaxMode = "1".equals(req.getParameter("_ajaxreq")); boolean conTest = "1".equals(req.getParameter("_jConTest")); SessionContext sc = null; try { boolean isNew = false; boolean isReloaded = false; String sessionID = req.getParameter("_msid"); String clientID = req.getSession().getId(); if (sessionID != null) { sc = jRuntime.getSessionContext(clientID, sessionID, req); if (sc == null) { // mark as reloaded so the user can be notified. isReloaded = true; } } // This is a connection test. Do not initialize the app if it isn't initialized yet. if (conTest) { respondConnectionTest(sc, req, res); } else { if (sc == null) { sc = initSession(req); isNew = true; } handleAuthentication(sc, req, res); if (!isNew && !resourceMode && !isReloaded) { handleAction(sc, req, res, upload); } boolean render = handleRedirect(sc, req, res); if (render) { // render control if (resourceMode) { renderResourceControl(sc, req, res, isReloaded); } else if (ajaxMode) { renderAjax(sc, req, res, isReloaded); } else { render(sc, req, res, isReloaded); } } } } catch (FileNotFoundException fnf) { try { res.sendError(HttpServletResponse.SC_NOT_FOUND, fnf.getMessage()); } catch (IOException ioe) { log.error("Error sending error to client", ioe); } log.warn("File not found: " + fnf.getMessage()); } catch (NotAuthenticatedException nae) { authFailed(req, res); } catch (Exception e) { log.error("Error in doPost()", e); displayError(req, res, e, sc); } long time = System.currentTimeMillis() - start; log.debug("total processing time: " + time + "ms"); } /** * @param sc * @param req * @param res */ private void respondConnectionTest(SessionContext sc, HttpServletRequest req, HttpServletResponse res) { res.setContentType("text/json; charset=UTF-8"); try { PrintWriter pw = res.getWriter(); pw.println("{\"sessionInitialized\":" + (sc != null) + "}"); pw.flush(); pw.close(); } catch (IOException e) { log.error("Error sending response on connection test request", e); } } /** * Checks if a redirect/exit has to happen. * @param ms * @param req * @param res * @return */ private boolean handleRedirect(SessionContext sc, HttpServletRequest req, HttpServletResponse res) { boolean render = true; boolean ajaxMode = "1".equals(req.getParameter("_ajaxreq")); // check if redirect had been set if (sc.getRedirectToURL() != null && !ajaxMode) { try { res.sendRedirect(sc.getRedirectToURL()); sc.clearRedirect(); } catch (IOException ioe) { // sth. went wrong throw new RuntimeException("Redirect to " + sc.getRedirectToURL() + " not successful: " + ioe); } render = false; } else if (sc.isDoExit() && !ajaxMode) { // display a message say "application has exited." displayError(req, res, new JWicException("The application has been terminated and no exit/redirect URL is given."), null); } // check if application should be closed if (sc.isDoExit() && !ajaxMode) { // close session sc.destroy(); render = false; } return render; } /** * Displays a page that describes the error. * @param req * @param res * @param e */ public void displayError(HttpServletRequest req, HttpServletResponse res, Exception e, SessionContext sc) { boolean ajaxMode = "1".equals(req.getParameter("_ajaxreq")); String responseFormat = req.getParameter("_format"); if (RESPONSE_FORMAT_JSON.equals(responseFormat)) { res.setContentType("text/json; charset=UTF-8"); } else { // compatibility mode. res.setContentType("text/xml; charset=UTF-8"); } if (RESPONSE_FORMAT_JSON.equals(responseFormat)) { PrintWriter pw; try { pw = res.getWriter(); JSONWriter jsonOut = new JSONWriter(pw); jsonOut.object(); jsonOut.key("exception") .value(e.toString()); jsonOut.endObject(); } catch (Exception er) { log.error("Error getting writer!", er); return; } } else { // compatibility mode res.setContentType(ajaxMode ? "text/xml" : "text/html"); PrintWriter pw; try { pw = res.getWriter(); } catch (Exception ex) { log.error("Error getting writer!"); return; } VelocityContext ctx = new VelocityContext(); ctx.put("jwic", new JWicTools(Locale.getDefault())); if (sc != null) { ctx.put("context", sc); } ctx.put("exception", e); try { String errorFile = ajaxMode? "WEB-INF/jwic/pages/jwic.ajax.page" : "WEB-INF/jwic/pages/jwic_error.page"; Template pageBase = ve.getTemplate(errorFile); pageBase.merge(ctx, pw); } catch (Exception ex) { pw.println("Error displaying error information. " + ex); } } } /** * Handle the incoming actions. * @param ms * @param req * @param res * @return true if control should be rendered */ private void handleAction(SessionContext sc, HttpServletRequest req, HttpServletResponse res, Upload upload) { IActionController controller = sc.getActionController(); String srcCtrl = req.getParameter("__ctrlid"); String action = req.getParameter("__action"); String acpara = req.getParameter("__acpara"); String sysinfo = req.getParameter("__sysinfo"); String ticketNo = req.getParameter("__ticket"); String layerid = req.getParameter("layerid"); String mouseEvent = req.getParameter("__mouseevent"); ValueChangedQueue queue = new ValueChangedQueue(); // double post protection if (ticketNo != null && ticketNo.length() != 0) { long ticket = Long.parseLong(ticketNo); if (!sc.validateTicket(ticket, layerid)) { log.info("DPP: ticket numbers don't match. actions ignored."); return; } } else { log.warn("DPP: No ticket number in the request."); } if ("".equals(srcCtrl) && "redraw".equals(action)) { log.debug("Redraw required - ignoring post content."); return; } WebEngineEvent event = new WebEngineEvent(sc, srcCtrl, action, acpara); fireEvent(EngineEvent.preHandleAction, event); // store system informations if (sysinfo != null) { // refresh user agent UserAgentInfo userAgent = sc.getUserAgent(); StringTokenizer stk = new StringTokenizer(sysinfo, ";"); int tokenNo = 0; while (stk.hasMoreTokens()) { String tokenValue = stk.nextToken(); int value = 0; try { Double valueDouble = new Double(tokenValue); value = valueDouble.intValue(); }catch(NumberFormatException ex){ log.error("Error while parsing client size value: " + tokenValue + " for token: " + tokenNo); } switch (tokenNo) { case 0: // visible width userAgent.setClientWidth(value); break; case 1: // visible height userAgent.setClientHeight(value); break; case 2: userAgent.setClientLeft(value); break; case 3: userAgent.setClientTop(value); break; } tokenNo++; } if (sc.getTopControl() instanceof Page) { // update page Page page = (Page)sc.getTopControl(); page.setClientLeft(userAgent.getClientLeft()); page.setClientTop(userAgent.getClientTop()); page.setPageSize(new Dimension(userAgent.getClientWidth(), userAgent.getClientHeight())); } } // set mouse event in session context if (mouseEvent != null && mouseEvent.length() > 0) { StringTokenizer stk = new StringTokenizer(mouseEvent, ";"); int tokenNo = 0; String type = null; int x = 0; int y = 0; while (stk.hasMoreTokens()) { String value = stk.nextToken(); switch (tokenNo) { case 0: // mouse event type type = value; break; case 1: // mouse x x = Integer.parseInt(value); break; case 2: // mouse y y = Integer.parseInt(value); break; } tokenNo++; } // set mouse event sc.setMouseEvent(new MouseEvent(sc, type, x, y)); } else { // remove mouse event sc.setMouseEvent(null); } // store recieved "fields" into the coresponding controls for (Enumeration<?> e = req.getParameterNames(); e.hasMoreElements(); ) { String paramName = (String)e.nextElement(); try { controller.handleField(sc, queue, paramName, req.getParameterValues(paramName)); } catch (ControlNotFoundException cnfe) { log.warn("Can not assign field '" + paramName + "'. Control not found."); } } // store recieved files into the coresponding controls if (upload != null) { Map<String, UploadFile> files = upload.getFiles(); for (Iterator<String> it = files.keySet().iterator(); it.hasNext(); ) { String fieldname = it.next(); UploadFile file = files.get(fieldname); controller.handleFile(sc, fieldname, file); } } // fire the valueChangedEvents before the action processing queue.processQueue(); // notify the control that has created the "action" link that the // link has been clicked by the user. if (action != null && srcCtrl != null && srcCtrl.length() != 0) { controller.handleAction(sc, srcCtrl, action, acpara); } fireEvent(EngineEvent.postHandleAction, event); return; } /** * Render the jWic Application. * @param sc * @param req * @param res */ private void render(SessionContext sc, HttpServletRequest req, HttpServletResponse res, boolean markReloaded) { res.setContentType("text/html; charset=UTF-8"); RenderContext context; try { context = new RenderContext(req, res); } catch (Exception e) { log.error("Error creating renderContext", e); return; } String layerid = req.getParameter("layerid"); Control ctrl = (layerid == null || layerid.length() == 0) ? sc.getTopControl() : sc.getControlByLayerID(layerid); VelocityContext ctx = new VelocityContext(); ctx.put("jwic", new JWicTools(sc.getLocale(), sc.getTimeZone())); ctx.put("page", ctrl); ctx.put("content", new ContentRenderer(ctrl, context)); ctx.put("context", sc); ctx.put("layerid", layerid == null ? "" : layerid); ctx.put("reloaded", markReloaded ? "1" : "0"); ctx.put("contextPath", req.getContextPath()); ctx.put("renderContext", context); List<String> scriptQueue = sc.getScriptQueue(); if (!scriptQueue.isEmpty()) { StringWriter sw = new StringWriter(); JSONWriter writer = new JSONWriter(sw); try { writer.array(); for (String script : scriptQueue) { writer.value(script); } writer.endArray(); ctx.put("scriptQueue", sw.toString()); }catch(Exception e){ throw new RuntimeException("Error while configuring Json Option for Script Queue.", e); } sc.clearScriptQueue(); } String templateName; if (layerid == null || layerid.length() == 0) { templateName = sc.getProperty("pagefile", PAGE_FILE); } else { templateName = sc.getProperty("layerpagefiler", PAGE_LAYER_FILE); } // build path to page template String path = req.getServletPath(); int idx = path.lastIndexOf('/'); if (idx != -1) { templateName = path.substring(0, idx + 1) + templateName; } try { WebEngineEvent event = new WebEngineEvent(sc, false); fireEvent(EngineEvent.preRendering, event); Template pageBase = ve.getTemplate(templateName); pageBase.merge(ctx, context.getWriter()); fireEvent(EngineEvent.postRendering, event); } catch (Exception ex) { displayError(req, res, ex, sc); } sc.setRequireRedraw(false); } /** * Render the jWic Application and return it in XML format so that the * jWic ajax script can update the DHTML controls. * @param sc * @param req * @param res */ private void renderAjax(SessionContext sc, HttpServletRequest req, HttpServletResponse res, boolean markReloaded) { String responseFormat = req.getParameter("_format"); WebEngineEvent event = new WebEngineEvent(sc, true); fireEvent(EngineEvent.preRendering, event); if (RESPONSE_FORMAT_JSON.equals(responseFormat)) { res.setContentType("text/json; charset=UTF-8"); } else { // compatibility mode. res.setContentType("text/xml; charset=UTF-8"); } PrintWriter pw; try { pw = res.getWriter(); } catch (Exception e) { log.error("Error getting writer!"); return; } String layerid = req.getParameter("layerid"); Control ctrl = (layerid == null || layerid.length() == 0) ? sc.getTopControl() : sc.getControlByLayerID(layerid); // create a list of all controls to be updated Map<String, Updateable> toUpdate = null; Exception renderingException = null; try { if (!sc.isRequireRedraw()) { toUpdate = new HashMap<String, Updateable>(); scanForUpdates(toUpdate, ctrl, req, res); } } catch (Exception e) { log.error("Error rendering controls.", e); renderingException = e; } if (RESPONSE_FORMAT_JSON.equals(responseFormat)) { JSONWriter jsonOut = new JSONWriter(pw); try { jsonOut.object() .key("ticket") .value(sc.getRequestTicket(layerid)); if (renderingException != null) { jsonOut.key("exception") .value(renderingException.toString()); } if (sc.isRequireRedraw()) { jsonOut.key("requireRedraw") .value(sc.getSessionId()); } if (ctrl instanceof Page) { Page page = (Page)ctrl; if (page.getForceFocusElement() != null && page.getForceFocusElement().length() != 0) { jsonOut.key("forceFocus") .value(page.getForceFocusElement()); } } if (toUpdate != null) { // merge required static JS files Set<String> allRequiredLibs = new HashSet<String>(); for (String key : toUpdate.keySet()) { Updateable updateable = toUpdate.get(key); if (updateable.requiredStaticJS != null) { allRequiredLibs.addAll(updateable.requiredStaticJS); } } if (!allRequiredLibs.isEmpty()) { jsonOut.key("requiredJS") .array(); for (String lib : allRequiredLibs) { jsonOut.value(lib); } jsonOut.endArray(); } jsonOut.key("updateables") .array(); for (String key : toUpdate.keySet()) { Updateable updateable = toUpdate.get(key); jsonOut.object(); jsonOut.key("key").value(key); jsonOut.key("html").value(updateable.htmlCode); if (updateable.javaScript != null && updateable.javaScript.size() > 0) { jsonOut.key("scripts") .array(); for (Map.Entry<String, String> entry : updateable.javaScript.entrySet()) { jsonOut.object(); jsonOut.key("controlId"); jsonOut.value(entry.getKey()); jsonOut.key("script"); jsonOut.value(entry.getValue()); jsonOut.endObject(); } jsonOut.endArray(); } jsonOut.endObject(); } jsonOut.endArray(); } List<String> scriptQueue = sc.getScriptQueue(); if (!scriptQueue.isEmpty()) { jsonOut.key("scriptQueue").array(); for (String line : scriptQueue) { jsonOut.value(line); } jsonOut.endArray(); sc.clearScriptQueue(); } jsonOut.endObject(); } catch (JSONException e) { log.error("Error generating AJAX response", e); displayError(req, res, e, sc); } } else { // compatibility mode. VelocityContext ctx = new VelocityContext(); ctx.put("jwic", new JWicTools(sc.getLocale(), sc.getTimeZone())); ctx.put("page", ctrl); ctx.put("context", sc); ctx.put("layerid", layerid == null ? "" : layerid); ctx.put("reloaded", markReloaded ? "1" : "0"); ctx.put("contextPath", req.getContextPath()); if (toUpdate != null) { ctx.put("updateables", toUpdate); } String templateName = "WEB-INF/jwic/pages/jwic.ajax.page"; try { Template pageBase = ve.getTemplate(templateName); pageBase.merge(ctx, pw); } catch (Exception ex) { displayError(req, res, ex, sc); } } fireEvent(EngineEvent.postRendering, event); } /** * Render a jWic resource control. * @param sc * @param req * @param res * @param markReloaded */ private void renderResourceControl(SessionContext sc, HttpServletRequest req, HttpServletResponse res, boolean markReloaded) { String controlId = req.getParameter(IResourceControl.URL_CONTROLID_PARAM); try { Control ctrl = controlId != null ? sc.getControlById(controlId) : null; if (ctrl instanceof IResourceControl) { IResourceControl control = (IResourceControl)ctrl; WebEngineEvent event = new WebEngineEvent(sc, controlId); fireEvent(EngineEvent.preResourceRequest, event); try { control.attachResource(req, res); } catch (Throwable t) { log.error("Error during IResourceControl.attachResource", t); } fireEvent(EngineEvent.postResourceRequest, event); } } catch (ControlNotFoundException cnfe) { // the control does no longer exist, send back notification log.debug("ResourceRequest to control id '" + controlId + "' failed. A control with this id does not exist."); try { res.sendError(HttpServletResponse.SC_NOT_FOUND, "A control with this ID can not be found."); } catch (IOException e) { log.error("Error sending control not found exception.", e); } return; } } /** * Scan the control tree for controls that require rendering. * If the control requires redraw it's rendered. * If it's visible and instance of IControlContainer its controls * are scaned for updates (more time intensive!). * @param toUpdate * @param ctrl */ private void scanForUpdates(Map<String, Updateable> toUpdate, Control ctrl, HttpServletRequest req, HttpServletResponse res) { long start = System.currentTimeMillis(); boolean showTime = false; if (showTime = ctrl.isRequireRedraw()) { ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintWriter pw = new PrintWriter(out); RenderContext context = new RenderContext(req, res, pw); ContentRenderer cr = new ContentRenderer(ctrl, context); try { WebEngineEvent event = new WebEngineEvent(ctrl.getSessionContext(), ctrl.getControlID()); fireEvent(EngineEvent.preControlRendering, event); cr.render(); fireEvent(EngineEvent.postControlRendering, event); } catch (Throwable t) { log.error("Error rendering control " + ctrl.getControlID() + " type " + ctrl.getClass().getName(), t); pw.print("Error rendering control: " + t.toString()); } pw.flush(); Updateable updateable = new Updateable(out.toString()); updateable.javaScript = context.getScripts(); updateable.requiredStaticJS = context.getRequiredStaticJs(); toUpdate.put(ctrl.getControlID(), updateable); } else { if (!ctrl.isVisible()) { return; } if (ctrl instanceof IControlContainer) { IControlContainer container = (IControlContainer)ctrl; for (Iterator<Control> it = container.getControls(); it.hasNext(); ) { Control control = it.next(); if (container.isRenderingRelevant(control)) { scanForUpdates(toUpdate, control, req, res); } } } } if (showTime /* TODO nicer implementation */) { long time = System.currentTimeMillis() - start; Runtime rt = Runtime.getRuntime(); log.debug("scan for updates time: " + time + "ms for " + ctrl.getControlID() + ", Java free heap: " + rt.freeMemory() + " of max " + rt.maxMemory()); } } /** * Initialize the module session by launching the specified jWic application. * @param req * @return * @throws NotAuthenticatedException * @throws FileNotFoundException */ private SessionContext initSession(HttpServletRequest req) throws NotAuthenticatedException, IOException { // get configuration file IApplicationSetup appSetup = appSetupProvider.createApplicationSetup(req); if (appSetup.isRequireAuthentication()) { if (authenticator == null || !authenticator.isAuthenticated(req)) { throw new NotAuthenticatedException(); } } HttpSession session = req.getSession(); Locale locale; TimeZone timeZone; if (session.getAttribute(Locale.class.getName()) instanceof Locale) { locale = (Locale)session.getAttribute(Locale.class.getName()); } else { // use the default locale if there is no locale specified for the // current session. locale = Locale.getDefault(); } if (session.getAttribute(TimeZone.class.getName()) instanceof TimeZone) { timeZone = (TimeZone)session.getAttribute(TimeZone.class.getName()); } else { timeZone = TimeZone.getDefault(); } SessionContext sc = jRuntime.createSessionContext(appSetup, locale, timeZone, req); sc.setCallBackURL(getFileName(req) + "?_msid=" + sc.getSessionId()); return sc; } /** * @param req * @return */ private String getFileName(HttpServletRequest req) { String path = req.getServletPath(); int i = path.lastIndexOf('/'); if (i != -1) { return path.substring(i + 1); } return path; } /** * Set the Authenticator. * @param authenticator */ public void setAuthenticator(IAuthenticator authenticator) { this.authenticator = authenticator; } /** * Set the loginPage. * @param loginPage */ public void setLoginPage(String loginPage) { this.loginPage = loginPage; } /* (non-Javadoc) * @see de.jwic.security.Authentication#authenticate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public void authFailed(HttpServletRequest req, HttpServletResponse res) { try { if (loginPage != null) { // set current url in ClientSession for later redirect req.getSession().setAttribute(IAuthenticator.SESSION_REDIRECT_URL, req.getRequestURI()); // show login page res.sendRedirect(loginPage); } else { res.getWriter().println("Configuration failure: No login page configured."); return; } } catch (IOException ioe) { log.error("Error redirecting to login page: " + loginPage, ioe); } } /** * Fix servlet container problems, e.g. jetty 6.1.2rc3 (and earlier) ajp13 usage. * @param req * @param res */ private void servletContainerFixes(HttpServletRequest req, HttpServletResponse res) { // correct character encoding and try to read it from header String enc = req.getCharacterEncoding(); if (enc == null) { String contentType = req.getHeader("Content-Type"); if (contentType != null) { contentType = contentType.toLowerCase(); int i = contentType.indexOf("charset"); if (i != -1) { String s = contentType.substring(i + 7).trim(); s = s.substring(s.indexOf('=') + 1).trim(); i = s.indexOf(';'); if (i != -1) { s = s.substring(0, i).trim(); } enc = s; try { req.setCharacterEncoding(enc); } catch (UnsupportedEncodingException e) { log.warn("Cannot set encoding", e); } } } } } }