0001: /*
0002: * This file is part of PFIXCORE.
0003: *
0004: * PFIXCORE is free software; you can redistribute it and/or modify
0005: * it under the terms of the GNU Lesser General Public License as published by
0006: * the Free Software Foundation; either version 2 of the License, or
0007: * (at your option) any later version.
0008: *
0009: * PFIXCORE is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012: * GNU Lesser General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU Lesser General Public License
0015: * along with PFIXCORE; if not, write to the Free Software
0016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017: *
0018: */
0019:
0020: package de.schlund.pfixxml;
0021:
0022: import java.io.FileNotFoundException;
0023: import java.io.IOException;
0024: import java.nio.charset.Charset;
0025: import java.text.NumberFormat;
0026: import java.text.SimpleDateFormat;
0027: import java.util.Date;
0028: import java.util.Enumeration;
0029: import java.util.HashMap;
0030: import java.util.Iterator;
0031: import java.util.LinkedList;
0032: import java.util.Map;
0033: import java.util.Properties;
0034: import java.util.TreeSet;
0035: import java.util.concurrent.atomic.AtomicInteger;
0036:
0037: import javax.servlet.ServletConfig;
0038: import javax.servlet.ServletContext;
0039: import javax.servlet.ServletException;
0040: import javax.servlet.http.Cookie;
0041: import javax.servlet.http.HttpServlet;
0042: import javax.servlet.http.HttpServletRequest;
0043: import javax.servlet.http.HttpServletResponse;
0044: import javax.servlet.http.HttpSession;
0045:
0046: import org.apache.log4j.Logger;
0047: import org.xml.sax.SAXException;
0048:
0049: import de.schlund.pfixxml.config.ServletManagerConfig;
0050: import de.schlund.pfixxml.config.XMLPropertiesUtil;
0051: import de.schlund.pfixxml.exceptionprocessor.ExceptionConfig;
0052: import de.schlund.pfixxml.exceptionprocessor.ExceptionProcessor;
0053: import de.schlund.pfixxml.perflogging.PerfEvent;
0054: import de.schlund.pfixxml.perflogging.PerfEventType;
0055: import de.schlund.pfixxml.resources.FileResource;
0056: import de.schlund.pfixxml.resources.ResourceUtil;
0057: import de.schlund.pfixxml.serverutil.SessionAdmin;
0058: import de.schlund.pfixxml.serverutil.SessionHelper;
0059: import de.schlund.pfixxml.serverutil.SessionInfoStruct;
0060: import de.schlund.pfixxml.serverutil.SessionInfoStruct.TrailElement;
0061: import de.schlund.pfixxml.util.MD5Utils;
0062:
0063: /**
0064: * ServletManager.java
0065: *
0066: *
0067: * Created: Wed May 8 16:39:06 2002
0068: *
0069: * @author <a href="mailto:jtl@schlund.de">Jens Lautenbacher</a>
0070: */
0071:
0072: public abstract class ServletManager extends HttpServlet {
0073: public static final String VISIT_ID = "__VISIT_ID__";
0074: public static final String PROP_LOADINDEX = "__PROPERTIES_LOAD_INDEX";
0075: private static final String STORED_REQUEST = "__STORED_PFIXSERVLETREQUEST__";
0076: private static final String SECURE_SESS_COOKIE = "__PFIX_SEC_";
0077: private static final String TEST_COOKIE = "__PFIX_TST_";
0078: private static final String SESSION_COOKIES_MARKER = "__COOKIES_USED_DURING_SESSION__";
0079: private static final String REFUSE_COOKIES = "__REFUSE_COOKIES__";
0080: private static final String RAND_SESS_COOKIE_VALUE = "__RAND_SESS_COOKIE_VALUE__";
0081: private static final String CHECK_FOR_RUNNING_SSL_SESSION = "__CHECK_FOR_RUNNING_SSL_SESSION__";
0082: private static final String PARAM_FORCELOCAL = "__forcelocal";
0083: public static final String PROP_COOKIE_SEC_NOT_ENFORCED = "servletmanager.cookie_security_not_enforced";
0084: public static final String PROP_P3PHEADER = "servletmanager.p3p";
0085: private static final String PROP_EXCEPTION = "exception";
0086: public static final String PROP_SSL_REDIRECT_PORT = "pfixcore.ssl_redirect_port.for.";
0087: protected static final String DEF_CONTENT_TYPE = "text/html";
0088: private static final String DEFAULT_ENCODING = "UTF-8";
0089: private static final String SERVLET_ENCODING = "servlet.encoding";
0090: private static final int MAX_PARALLEL_SEC_SESSIONS = 10;
0091: private static String TIMESTAMP_ID = "";
0092: private static int INC_ID = 0;
0093: private boolean cookie_security_not_enforced = false;
0094: private Logger LOGGER_VISIT = Logger.getLogger("LOGGER_VISIT");
0095: private Logger LOG = Logger.getLogger(ServletManager.class);
0096: private Map<Class<? extends Throwable>, ExceptionConfig> exceptionConfigs = new HashMap<Class<? extends Throwable>, ExceptionConfig>();
0097: private long common_mtime = -2;
0098: private long servlet_mtime = -2;
0099: private FileResource commonpropfile;
0100: private FileResource servletpropfile;
0101: private String servletEncoding;
0102: private AtomicInteger configLoadIndex = new AtomicInteger(0);
0103:
0104: protected abstract ServletManagerConfig getServletManagerConfig();
0105:
0106: protected abstract void reloadServletConfig(
0107: FileResource configFile, Properties globalProperties)
0108: throws ServletException;
0109:
0110: protected boolean runningUnderSSL(HttpServletRequest req) {
0111: return req.isSecure();
0112: }
0113:
0114: protected boolean needsSSL(PfixServletRequest preq)
0115: throws ServletException {
0116: return this .getServletManagerConfig().isSSL();
0117: }
0118:
0119: abstract protected boolean needsSession();
0120:
0121: abstract protected boolean allowSessionCreate();
0122:
0123: public static String getServerName(HttpServletRequest req) {
0124: String forward = req.getHeader("X-Forwarded-Server");
0125: if (forward != null && !forward.equals("")) {
0126: return forward;
0127: } else {
0128: return req.getServerName();
0129: }
0130: }
0131:
0132: protected void relocate(HttpServletResponse res, String reloc_url) {
0133: LOG.debug("\n\n ======> relocating to " + reloc_url
0134: + "\n");
0135: res.setHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");
0136: res.setHeader("Pragma", "no-cache");
0137: res.setHeader("Cache-Control",
0138: "no-cache, no-store, private, must-revalidate");
0139: res.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
0140: res.setHeader("Location", reloc_url);
0141: }
0142:
0143: public void doPost(HttpServletRequest req, HttpServletResponse res)
0144: throws ServletException, IOException {
0145: doGet(req, res);
0146: }
0147:
0148: public void doGet(HttpServletRequest req, HttpServletResponse res)
0149: throws ServletException, IOException {
0150: req.setCharacterEncoding(servletEncoding);
0151: res.setCharacterEncoding(servletEncoding);
0152: if (LOG.isDebugEnabled()) {
0153: LOG
0154: .debug("\n ------------------- Start of new Request ---------------");
0155: LOG.debug("====> Scheme://Server:Port " + req.getScheme()
0156: + "://" + getServerName(req) + ":"
0157: + req.getServerPort());
0158: LOG.debug("====> URI: " + req.getRequestURI());
0159: LOG.debug("====> Query: " + req.getQueryString());
0160: LOG.debug("----> needsSession=" + needsSession()
0161: + " allowSessionCreate=" + allowSessionCreate());
0162: LOG.debug("====> Sessions: "
0163: + SessionAdmin.getInstance().toString());
0164: LOG.debug("\n");
0165:
0166: Enumeration<?> headers = req.getHeaderNames();
0167:
0168: while (headers.hasMoreElements()) {
0169: String header = (String) headers.nextElement();
0170: String headerval = req.getHeader(header);
0171: LOG.debug("+++ Header: " + header + " -> " + headerval);
0172: }
0173:
0174: }
0175:
0176: HttpSession session = null;
0177: boolean has_session = false;
0178: boolean has_ssl_session_insecure = false;
0179: boolean has_ssl_session_secure = false;
0180: boolean force_jump_back_to_ssl = false;
0181: boolean force_reuse_visit_id = false;
0182: String mark_session_as_no_cookies = null;
0183: boolean does_cookies = false;
0184: boolean refuse_cookies = false;
0185:
0186: // Set P3P-Header if needed to make sure it is
0187: // set for every response (even redirects).
0188: String p3pHeader = getServletManagerConfig().getProperties()
0189: .getProperty(PROP_P3PHEADER);
0190: if (p3pHeader != null && p3pHeader.length() > 0) {
0191: res.addHeader("P3P", p3pHeader);
0192: }
0193:
0194: // Delete JSESSIONID cookie
0195: // Otherwise a redirect loop will be caused when a request with an
0196: // invalid JSESSIONID cookie is made
0197: if (req.getCookies() != null) {
0198: Cookie[] cookies = req.getCookies();
0199: for (int i = 0; i < cookies.length; i++) {
0200: Cookie cookie = cookies[i];
0201: if (cookie.getName().equalsIgnoreCase("JSESSIONID")) {
0202: cookie.setMaxAge(0);
0203: cookie
0204: .setPath((req.getContextPath().equals("")) ? "/"
0205: : req.getContextPath());
0206: res.addCookie(cookie);
0207: }
0208: }
0209: }
0210:
0211: if (req.isRequestedSessionIdValid()) {
0212: session = req.getSession(false);
0213: has_session = true;
0214: LOG.debug("*** Found valid session with ID "
0215: + session.getId());
0216:
0217: // Much of the advanced security depends on having cookies enabled. We need to make
0218: // sure that this isn't defeated by just disabling cookies. So we mark every session
0219: // whenever the client has cookies enabled, and don't allow further uses of this session
0220: // without cookies. BUT: having a valid session that has the attribute
0221: // __REFUSE_COOKIES__ set, will be considered as not doing cookies at all. See
0222: // below where mark_session_as_no_cookies is set for the reason behind this.
0223: Boolean refuse_param = (Boolean) session
0224: .getAttribute(REFUSE_COOKIES);
0225: if (refuse_param != null && refuse_param.booleanValue()) {
0226: does_cookies = false;
0227: refuse_cookies = true;
0228: } else {
0229: refuse_cookies = false;
0230: does_cookies = doCookieTest(req, res, session);
0231: }
0232: Boolean secure = (Boolean) session
0233: .getAttribute(SessionAdmin.SESSION_IS_SECURE);
0234:
0235: if (!does_cookies && !refuse_cookies) {
0236: LOG.debug("*** Client doesn't use cookies...");
0237: // We still need to check if the session itself thinks differently -
0238: // this happens e.g. when cookies are disabled in the middle of the session.
0239: Boolean need_cookies = (Boolean) session
0240: .getAttribute(SESSION_COOKIES_MARKER);
0241: if (need_cookies != null && need_cookies.booleanValue()) {
0242: if (cookie_security_not_enforced) {
0243: LOG
0244: .debug(" ... during the session cookies were ENABLED, "
0245: + "but will continue because of cookie_security_not_enforced "
0246: + session.getId());
0247: } else {
0248: LOG
0249: .debug(" ... but during the session cookies were already ENABLED: "
0250: + "Will invalidate the session "
0251: + session.getId());
0252: session.invalidate();
0253: has_session = false;
0254: }
0255: } else {
0256: LOG
0257: .debug(" ... and during the session cookies were DISABLED, too: Let's hope everything is OK...");
0258: }
0259: } else if (!does_cookies && refuse_cookies) {
0260: LOG.debug("*** Session REFUSES to use cookies!");
0261: LOG
0262: .debug(" Client may send cookies, but session refuses to handle them.");
0263: } else {
0264: LOG.debug("*** Client uses cookies.");
0265: }
0266: if (has_session) {
0267: if (runningUnderSSL(req)) {
0268: LOG.debug("*** Found running under SSL");
0269: if (secure != null && secure.booleanValue()) {
0270: LOG.debug(" ... and session is secure.");
0271: if (does_cookies) {
0272: LOG
0273: .debug("*** Client does cookies: Double checking SSL cookie for session ID");
0274: String sec_testid = (String) session
0275: .getAttribute(SECURE_SESS_COOKIE
0276: + MD5Utils.hex_md5(session
0277: .getId()));
0278: LOG
0279: .debug("*** Session expects to see the cookie value "
0280: + sec_testid);
0281: Cookie cookie = getSecureSessionCookie(req,
0282: session.getId());
0283: cleanupCookies(req, res, cookie);
0284: if (cookie != null) {
0285: LOG
0286: .debug("*** Found a matching cookie ...");
0287: String tmp = cookie.getValue();
0288: String tmp_sec = tmp.substring(tmp
0289: .indexOf(":") + 1);
0290: if (tmp_sec.equals(sec_testid)) {
0291: LOG
0292: .debug(" ... and the value is correct! ("
0293: + tmp_sec + ")");
0294: has_ssl_session_secure = true;
0295: Cookie cookie_new = new Cookie(
0296: cookie.getName(),
0297: System.currentTimeMillis()
0298: + ":" + tmp_sec);
0299: setCookiePath(req, cookie_new);
0300: // FIXME (see comment in cleanupCookies
0301: // cookie_new.setMaxAge(session.getMaxInactiveInterval());
0302: cookie_new.setMaxAge(-1);
0303: cookie_new.setSecure(true);
0304: res.addCookie(cookie_new);
0305: } else {
0306: LOG
0307: .debug(" ... but the value is WRONG!");
0308: LOG
0309: .error("*** Wrong Session-ID for running secure session from cookie. "
0310: + "IP:"
0311: + req
0312: .getRemoteAddr()
0313: + " Cookie: "
0314: + cookie.getValue()
0315: + " SessID: "
0316: + session.getId());
0317: session.invalidate();
0318: has_session = false;
0319: }
0320: } else {
0321: LOG
0322: .debug("*** Found NO matching cookie at all, but client does cookies: ***");
0323: LOG
0324: .error("*** NOSECSESSIDFROMCOOKIE: "
0325: + req.getRemoteAddr()
0326: + "|"
0327: + session.getId()
0328: + "|"
0329: + req
0330: .getHeader("User-Agent")
0331: + "|"
0332: + req
0333: .getHeader("Cookie"));
0334: // Most time when this happens, we are not under attack, but one of
0335: // two things happened: a) a stupid behaviour (bug?) of IE or opera
0336: // strikes us bad: With these two browsers, if we accept the
0337: // __PFIX_TST_* cookie, but then deny the __PFIX_SEC_* cookie AND
0338: // also deny for all further cookies from the domain, the stupid
0339: // browser will still continue to send the __PFIX_TST_* cookie, so
0340: // we will continue to come into this branch over and over
0341: // again... or b) We simply have stored too many cookies for the
0342: // secure cookie to be send by the client. So we try to mark the now
0343: // created session to decide in the following requests that this
0344: // session does NOT use cookies at all, despite what ever the
0345: // __PFIX_TST_* cookie says. Basically we completely switch off
0346: // cookie handling for this new session.
0347: mark_session_as_no_cookies = (String) session
0348: .getAttribute(VISIT_ID);
0349: session.invalidate();
0350: has_session = false;
0351: }
0352: } else {
0353: // We don't do cookies, so we simply have to believe it...
0354: has_ssl_session_secure = true;
0355: }
0356: } else {
0357: LOG.debug(" ... but session is insecure!");
0358: has_ssl_session_insecure = true;
0359: }
0360: } else if (secure != null && secure.booleanValue()) {
0361: LOG
0362: .debug("*** Found secure session but NOT running under SSL => Destroying session.");
0363: session.invalidate();
0364: has_session = false;
0365: }
0366: }
0367: } else if (req.getRequestedSessionId() != null) {
0368: LOG.debug("*** Found old and invalid session in request");
0369: // We have no valid session, but the request contained an invalid session id.
0370: // case a) This may be an invalid id because we invalidated the session when jumping
0371: // into the secure SSL session (see redirectToSecureSSLSession below). by using the back button
0372: // of the browser, the user may have come back to a (non-ssl) page (in his browser history) that contains
0373: // links with the old "parent" session id embedded. We need to check for this and create a
0374: // new session but reuse the visit id of the currently running SSL session.
0375: if (!runningUnderSSL(req)
0376: && SessionAdmin.getInstance().idWasParentSession(
0377: req.getRequestedSessionId())) {
0378: LOG
0379: .debug(" ... but this session was the parent of a currently running secure session.");
0380: HttpSession secure_session = SessionAdmin.getInstance()
0381: .getChildSessionForParentId(
0382: req.getRequestedSessionId());
0383: if (secure_session != null) {
0384: does_cookies = doCookieTest(req, res,
0385: secure_session);
0386: }
0387: // We'll try to get back there securely by first jumping back to a new (insecure) SSL session,
0388: // and after that the the jump to the secure SSL session will not create a new one, but reuse
0389: // the already running secure session instead (but only if a secure cookie can identify the request as
0390: // coming from the browser that made the initial jump http->https).
0391: if (does_cookies) {
0392: LOG
0393: .debug(" ... client handles cookies, so we'll check if we can reuse the parent session.");
0394: force_jump_back_to_ssl = true;
0395: } else {
0396: // OK, it seems as if we will not be able to identify the peer by comparing cookies.
0397: // So the only thing we can do is to reuse the VISIT_ID.
0398: LOG
0399: .debug(" ... but can't reuse the secure session because the client doesn't handle cookies.");
0400: force_reuse_visit_id = true;
0401: }
0402: } else {
0403: // Normally the balancer (or, more accurate: mod_jk) has a chance to choose the right server for a
0404: // new session, but with a session id in the URL it wasn't able to. So we redirect to a "fresh" request
0405: // without _any_ id, giving the balancer the possibility to choose a different server. (this can be
0406: // overridden by supplying the parameter __forcelocal=1 to the request). All this makes only sense of
0407: // course if we are running in a cluster of servers behind a balancer that chooses the right server
0408: // based on the session id included in the URL.
0409: String forcelocal = req.getParameter(PARAM_FORCELOCAL);
0410: if (forcelocal != null
0411: && (forcelocal.equals("1")
0412: || forcelocal.equals("true") || forcelocal
0413: .equals("yes"))) {
0414: LOG
0415: .debug(" ... but found __forcelocal parameter to be set.");
0416: } else {
0417: LOG.debug(" ... and __forcelocal is NOT set.");
0418: redirectToClearedRequest(req, res);
0419: return;
0420: // End of request cycle.
0421: }
0422: }
0423: }
0424:
0425: PfixServletRequest preq = null;
0426: if (has_session) {
0427: preq = (PfixServletRequest) session
0428: .getAttribute(STORED_REQUEST);
0429: if (preq != null) {
0430: LOG
0431: .debug("*** Found old PfixServletRequest object in session");
0432: session.removeAttribute(STORED_REQUEST);
0433: preq.updateRequest(req);
0434: }
0435: }
0436: if (preq == null) {
0437: LOG.debug("*** Creating PfixServletRequest object.");
0438: preq = new PfixServletRequestImpl(req, this
0439: .getServletManagerConfig().getProperties());
0440: }
0441:
0442: FactoryInitServlet.tryReloadLog4j();
0443: tryReloadProperties(preq);
0444:
0445: // End of initialization. Now we handle all cases where we need to redirect.
0446:
0447: if (force_jump_back_to_ssl && allowSessionCreate()) {
0448: LOG.debug("=> I");
0449: forceRedirectBackToInsecureSSL(preq, req, res);
0450: return;
0451: // End of request cycle.
0452: }
0453: if (force_reuse_visit_id && allowSessionCreate()) {
0454: LOG.debug("=> II");
0455: forceNewSessionSameVisit(preq, req, res);
0456: return;
0457: // End of request cycle.
0458: }
0459: if (has_ssl_session_insecure) {
0460: LOG.debug("=> III");
0461: redirectToSecureSSLSession(preq, req, res,
0462: mark_session_as_no_cookies);
0463: return;
0464: // End of request cycle.
0465: }
0466: if (needsSession() && allowSessionCreate() && needsSSL(preq)
0467: && !has_ssl_session_secure) {
0468: LOG.debug("=> IV");
0469: redirectToInsecureSSLSession(preq, req, res,
0470: mark_session_as_no_cookies);
0471: return;
0472: // End of request cycle.
0473: }
0474: if (!has_session && needsSession() && allowSessionCreate()
0475: && !needsSSL(preq)) {
0476: LOG.debug("=> V");
0477: redirectToSession(preq, req, res,
0478: mark_session_as_no_cookies);
0479: return;
0480: // End of request cycle.
0481: }
0482: if (!has_session && !needsSession() && needsSSL(preq)
0483: && !runningUnderSSL(req)) {
0484: LOG.debug("=> VI");
0485: redirectToSSL(req, res);
0486: return;
0487: // End of request cycle.
0488: }
0489:
0490: LOG
0491: .debug("*** >>> End of redirection management, handling request now.... <<< ***\n");
0492:
0493: PerfEvent pe = new PerfEvent(
0494: PerfEventType.XMLSERVER_CALLPROCESS, req
0495: .getRequestURI());
0496: pe.start();
0497: callProcess(preq, req, res);
0498: pe.save();
0499: }
0500:
0501: private void redirectToClearedRequest(HttpServletRequest req,
0502: HttpServletResponse res) {
0503: LOG.debug("===> Redirecting to cleared Request URL");
0504: String redirect_uri = SessionHelper.getClearedURL(req
0505: .getScheme(), getServerName(req), req,
0506: getServletManagerConfig().getProperties());
0507: relocate(res, redirect_uri);
0508: }
0509:
0510: private void redirectToSSL(HttpServletRequest req,
0511: HttpServletResponse res) {
0512: LOG
0513: .debug("===> Redirecting to session-less request URL under SSL");
0514: String redirect_uri = SessionHelper.getClearedURL("https",
0515: getServerName(req), req, getServletManagerConfig()
0516: .getProperties());
0517: relocate(res, redirect_uri);
0518: }
0519:
0520: private void redirectToSecureSSLSession(PfixServletRequest preq,
0521: HttpServletRequest req, HttpServletResponse res,
0522: String msanc) {
0523: HttpSession session = req.getSession(false);
0524: String visit_id = (String) session.getAttribute(VISIT_ID);
0525: String parentid = (String) session
0526: .getAttribute(CHECK_FOR_RUNNING_SSL_SESSION);
0527: if (parentid != null && !parentid.equals("")) {
0528: LOG
0529: .debug("*** The current insecure SSL session says to check for a already running SSL session for reuse");
0530: HttpSession secure_session = SessionAdmin.getInstance()
0531: .getChildSessionForParentId(parentid);
0532: if (secure_session != null) {
0533: String secure_id = secure_session.getId();
0534: String sec_testid = (String) secure_session
0535: .getAttribute(SECURE_SESS_COOKIE
0536: + MD5Utils.hex_md5(secure_id));
0537: LOG.debug("*** We have found a candidate: SessionId="
0538: + secure_id + " now search for cookie...");
0539: LOG
0540: .debug("*** Session expects to see the cookie value "
0541: + sec_testid);
0542: // But we need to make sure that the current request comes
0543: // from the same user who created this secure session.
0544: // We do this by checking for a (secure) cookie with a corresponding session id.
0545: Cookie cookie = getSecureSessionCookie(req, secure_id);
0546: if (cookie != null) {
0547: LOG.debug("*** Found a matching cookie ...");
0548: String tmp = cookie.getValue();
0549: String tmp_sec = tmp
0550: .substring(tmp.indexOf(":") + 1);
0551: if (tmp_sec.equals(sec_testid)) {
0552: LOG.debug(" ... and the value is correct! ("
0553: + tmp_sec + ")");
0554: LOG
0555: .debug("==> Redirecting to the secure SSL URL with the already running secure session "
0556: + secure_id);
0557: String redirect_uri = SessionHelper.encodeURL(
0558: "https", getServerName(req), req,
0559: secure_id, getServletManagerConfig()
0560: .getProperties());
0561: relocate(res, redirect_uri);
0562: return;
0563: } else {
0564: LOG.debug(" ... but the value is WRONG!");
0565: // throw new RuntimeException("Wrong Session-ID for running secure session from cookie.");
0566: LOG
0567: .error("Wrong Session-ID for running secure session from cookie.");
0568: }
0569: } else {
0570: LOG
0571: .debug("*** NO matching SecureSessionCookie (not even a wrong one...)");
0572: }
0573: }
0574: }
0575:
0576: LOG.debug("*** Saving session data...");
0577: HashMap<String, Object> map = new HashMap<String, Object>();
0578: SessionHelper.saveSessionData(map, session);
0579: // Before we invalidate the current session we save the traillog
0580: SessionInfoStruct infostruct = SessionAdmin.getInstance()
0581: .getInfo(session);
0582: LinkedList<TrailElement> traillog = new LinkedList<TrailElement>();
0583: String old_id = session.getId();
0584: if (infostruct != null) {
0585: traillog = SessionAdmin.getInstance().getInfo(session)
0586: .getTraillog();
0587: } else {
0588: LOG.warn("*** Infostruct == NULL ***");
0589: }
0590:
0591: LOG.debug("*** Invalidation old session (Id: " + old_id + ")");
0592: session.invalidate();
0593: session = req.getSession(true);
0594:
0595: // First of all we put the old session id into the new session (__PARENT_SESSION_ID__)
0596: session.setAttribute(SessionAdmin.PARENT_SESS_ID, old_id);
0597: if (visit_id != null) {
0598: // Don't call this.registerSession(...) here. We don't want to log this as a different visit.
0599: // Now we register the new session with saved traillog
0600: SessionAdmin.getInstance().registerSession(session,
0601: traillog, infostruct.getData().getServerName(),
0602: infostruct.getData().getRemoteAddr());
0603: } else {
0604: // Register a new session now.
0605: registerSession(req, session);
0606: }
0607: LOG.debug("*** Got new Session (Id: " + session.getId() + ")");
0608: LOG.debug("*** Copying data back to new session");
0609: SessionHelper.copySessionData(map, session);
0610: session.setAttribute(SessionHelper.SESSION_ID_URL,
0611: SessionHelper.getURLSessionId(req));
0612: LOG.debug("*** Setting " + SessionHelper.SESSION_ID_URL
0613: + " to "
0614: + session.getAttribute(SessionHelper.SESSION_ID_URL));
0615: LOG.debug("*** Setting SECURE flag");
0616: session.setAttribute(SessionAdmin.SESSION_IS_SECURE,
0617: Boolean.TRUE);
0618: session.setAttribute(STORED_REQUEST, preq);
0619:
0620: if (msanc == null) {
0621: Cookie cookie = getSecureSessionCookie(req, session.getId());
0622: if (cookie != null) {
0623: setCookiePath(req, cookie);
0624: cookie.setMaxAge(0);
0625: cookie.setSecure(true);
0626: res.addCookie(cookie);
0627: }
0628:
0629: String sec_testid = Long
0630: .toHexString((long) (Math.random() * Long.MAX_VALUE));
0631: LOG.debug("*** Secure Test-ID used in session and cookie: "
0632: + sec_testid);
0633: String sec_cookie = MD5Utils.hex_md5(session.getId());
0634: session.setAttribute(SECURE_SESS_COOKIE + sec_cookie,
0635: sec_testid);
0636:
0637: cookie = new Cookie(SECURE_SESS_COOKIE + sec_cookie, System
0638: .currentTimeMillis()
0639: + ":" + sec_testid);
0640: setCookiePath(req, cookie);
0641: // FIXME (see comment in cleanupCookies
0642: //cookie.setMaxAge(session.getMaxInactiveInterval());
0643: cookie.setMaxAge(-1);
0644: cookie.setSecure(true);
0645: res.addCookie(cookie);
0646:
0647: // Make sure a test cookie is created for the new session if needed
0648: createTestCookie(req, res);
0649: }
0650:
0651: LOG
0652: .debug("===> Redirecting to secure SSL URL with session (Id: "
0653: + session.getId() + ")");
0654: String redirect_uri = SessionHelper.encodeURL("https",
0655: getServerName(req), req, getServletManagerConfig()
0656: .getProperties());
0657: relocate(res, redirect_uri);
0658: }
0659:
0660: private void redirectToInsecureSSLSession(PfixServletRequest preq,
0661: HttpServletRequest req, HttpServletResponse res,
0662: String msanc) {
0663: boolean reuse_session = false;
0664: if (req.isRequestedSessionIdValid()) {
0665: reuse_session = true;
0666: LOG
0667: .debug("*** reusing existing session for jump http=>https");
0668: }
0669: HttpSession session = req.getSession(true);
0670: if (!reuse_session) {
0671: if (msanc == null) {
0672: registerSession(req, session);
0673: } else {
0674: session.setAttribute(VISIT_ID, msanc);
0675: LOG
0676: .debug("*** Setting REFUSE COOKIES flag in session (Id: "
0677: + session.getId() + ")");
0678: session.setAttribute(REFUSE_COOKIES, Boolean.TRUE);
0679: SessionAdmin.getInstance().registerSession(session,
0680: getServerName(req), req.getRemoteAddr());
0681: }
0682: }
0683:
0684: session.setAttribute(SessionHelper.SESSION_ID_URL,
0685: SessionHelper.getURLSessionId(req));
0686: LOG.debug("*** Setting INSECURE flag in session (Id: "
0687: + session.getId() + ")");
0688: session.setAttribute(SessionAdmin.SESSION_IS_SECURE,
0689: Boolean.FALSE);
0690: session.setAttribute(STORED_REQUEST, preq);
0691:
0692: // Make sure a test cookie is created if needed
0693: if (msanc == null) {
0694: createTestCookie(req, res);
0695: }
0696:
0697: LOG
0698: .debug("===> Redirecting to insecure SSL URL with session (Id: "
0699: + session.getId() + ")");
0700: String redirect_uri = SessionHelper.encodeURL("https",
0701: getServerName(req), req, getServletManagerConfig()
0702: .getProperties());
0703: relocate(res, redirect_uri);
0704: }
0705:
0706: private void forceRedirectBackToInsecureSSL(
0707: PfixServletRequest preq, HttpServletRequest req,
0708: HttpServletResponse res) {
0709: // When we come here, we KNOW that there's a secure SSL session already running, so this session here is
0710: // only used for the jump to SSL so we can get the cookie to check the identity of the caller.
0711: String parentid = req.getRequestedSessionId();
0712: HttpSession session = req.getSession(true);
0713: session.setAttribute(SessionHelper.SESSION_ID_URL,
0714: SessionHelper.getURLSessionId(req));
0715: session.setAttribute(CHECK_FOR_RUNNING_SSL_SESSION, parentid);
0716: LOG.debug("*** Setting INSECURE flag in session (Id: "
0717: + session.getId() + ")");
0718: session.setAttribute(SessionAdmin.SESSION_IS_SECURE,
0719: Boolean.FALSE);
0720: session.setAttribute(STORED_REQUEST, preq);
0721:
0722: HttpSession child = SessionAdmin.getInstance()
0723: .getChildSessionForParentId(parentid);
0724: String testrand = (String) child
0725: .getAttribute(RAND_SESS_COOKIE_VALUE);
0726: if (testrand == null || testrand.equals("")) {
0727: // Make sure a test cookie is created
0728: createTestCookie(req, res);
0729: } else {
0730: session.setAttribute(RAND_SESS_COOKIE_VALUE, testrand);
0731: }
0732:
0733: LOG.debug("===> Redirecting to SSL URL with session (Id: "
0734: + session.getId() + ")");
0735: String redirect_uri = SessionHelper.encodeURL("https",
0736: getServerName(req), req, getServletManagerConfig()
0737: .getProperties());
0738: relocate(res, redirect_uri);
0739: }
0740:
0741: private void forceNewSessionSameVisit(PfixServletRequest preq,
0742: HttpServletRequest req, HttpServletResponse res) {
0743: // When we come here, we KNOW that there's a secure SSL session already running, but unfortunately
0744: // it seems that the browser doesn't send cookies. So we will not be able to know for sure that the request comes
0745: // from the legitimate user. The only thing we can do is to copy the VISIT_ID, which helps to keep the
0746: // statistic clean :-)
0747: String parentid = req.getRequestedSessionId();
0748: HttpSession child = SessionAdmin.getInstance()
0749: .getChildSessionForParentId(parentid);
0750: String curr_visit_id = (String) child.getAttribute(VISIT_ID);
0751: HttpSession session = req.getSession(true);
0752:
0753: String testrand = (String) child
0754: .getAttribute(RAND_SESS_COOKIE_VALUE);
0755: if (testrand == null || testrand.equals("")) {
0756: // Make sure a test cookie is created
0757: createTestCookie(req, res);
0758: } else {
0759: session.setAttribute(RAND_SESS_COOKIE_VALUE, testrand);
0760: }
0761:
0762: LinkedList<TrailElement> traillog = SessionAdmin.getInstance()
0763: .getInfo(child).getTraillog();
0764: session.setAttribute(SessionHelper.SESSION_ID_URL,
0765: SessionHelper.getURLSessionId(req));
0766: session.setAttribute(VISIT_ID, curr_visit_id);
0767: SessionAdmin.getInstance().registerSession(session, traillog,
0768: getServerName(req), req.getRemoteAddr());
0769: LOG.debug("===> Redirecting with session (Id: "
0770: + session.getId() + ") using OLD VISIT_ID: "
0771: + curr_visit_id);
0772: session.setAttribute(STORED_REQUEST, preq);
0773: String redirect_uri = SessionHelper.encodeURL(req.getScheme(),
0774: getServerName(req), req, getServletManagerConfig()
0775: .getProperties());
0776: relocate(res, redirect_uri);
0777: }
0778:
0779: private void redirectToSession(PfixServletRequest preq,
0780: HttpServletRequest req, HttpServletResponse res,
0781: String msanc) {
0782: HttpSession session = req.getSession(true);
0783: session.setAttribute(SessionHelper.SESSION_ID_URL,
0784: SessionHelper.getURLSessionId(req));
0785: if (msanc == null) {
0786: registerSession(req, session);
0787: createTestCookie(req, res);
0788: } else {
0789: session.setAttribute(VISIT_ID, msanc);
0790: LOG
0791: .debug("*** Setting REFUSE COOKIES flag in session (Id: "
0792: + session.getId() + ")");
0793: session.setAttribute(REFUSE_COOKIES, Boolean.TRUE);
0794: SessionAdmin.getInstance().registerSession(session,
0795: getServerName(req), req.getRemoteAddr());
0796: }
0797:
0798: LOG.debug("===> Redirecting to URL with session (Id: "
0799: + session.getId() + ")");
0800: session.setAttribute(STORED_REQUEST, preq);
0801: String redirect_uri = SessionHelper.encodeURL(req.getScheme(),
0802: getServerName(req), req, getServletManagerConfig()
0803: .getProperties());
0804: relocate(res, redirect_uri);
0805: }
0806:
0807: private boolean doCookieTest(HttpServletRequest req,
0808: HttpServletResponse res, HttpSession sess) {
0809: if (sess == null) {
0810: sess = req.getSession(false);
0811: }
0812: // If in this session the client has been found to do cookies already, don't check the test
0813: // cookie value again. We still have to check if there are any cookies at all (a
0814: // test cookie should be there, but maybe the wrong value because another session is
0815: // opened in parallel), because we need to guard against clients which supply cookies over
0816: // the whole redirect chain, but don't supply cookies on the following request, and we want
0817: // to correctly react on people who turn off cookies during the session.
0818: if (sess != null) {
0819: LOG.debug("*** Testing for marked session...");
0820: Cookie[] cookies = req.getCookies();
0821: boolean sessionusescookies = false;
0822: Boolean doescookies = (Boolean) sess
0823: .getAttribute(SESSION_COOKIES_MARKER);
0824: if (doescookies != null && doescookies.booleanValue()) {
0825: sessionusescookies = true;
0826: LOG
0827: .debug(" ...session is already marked as using cookies, looking for ANY test cookie...");
0828: } else {
0829: LOG
0830: .debug(" ...session is NOT already marked as using cookies!");
0831: }
0832:
0833: String rand = (String) sess
0834: .getAttribute(RAND_SESS_COOKIE_VALUE);
0835: if (rand != null) {
0836: LOG.debug("*** Testing for cookie " + TEST_COOKIE
0837: + "...");
0838: if (cookies != null) {
0839: for (int i = 0; i < cookies.length; i++) {
0840: Cookie cookie = cookies[i];
0841: if (cookie.getName().equals(TEST_COOKIE)) {
0842: if (sessionusescookies) {
0843: // No need to check the value...
0844: LOG
0845: .debug(" ... found it, no need to check the value (because session is marked).");
0846: return true;
0847: } else {
0848: LOG
0849: .debug(" ... found it, checking value "
0850: + rand);
0851: if (cookie.getValue().equals(rand)) {
0852: LOG
0853: .debug(" ... value matches! Marking session...");
0854: sess.setAttribute(
0855: SESSION_COOKIES_MARKER,
0856: Boolean.TRUE);
0857: return true;
0858: } else {
0859: LOG
0860: .debug(" ... value is WRONG.");
0861: }
0862: }
0863: break;
0864: }
0865: }
0866: LOG
0867: .debug("*** Client sends cookies, but not our test cookie! ***");
0868: }
0869: }
0870: }
0871: return false;
0872: }
0873:
0874: private boolean createTestCookie(HttpServletRequest req,
0875: HttpServletResponse res) {
0876: HttpSession sess = req.getSession(false);
0877: String rand = null;
0878: if (sess != null) {
0879: rand = (String) sess.getAttribute(RAND_SESS_COOKIE_VALUE);
0880: if (rand != null) {
0881: LOG
0882: .debug("*** Already found a test cookie value in session: "
0883: + rand);
0884: } else {
0885: rand = Long
0886: .toHexString((long) (Math.random() * Long.MAX_VALUE));
0887: LOG.debug("*** Creating a random test cookie value: "
0888: + rand);
0889: }
0890: Cookie newprobe = new Cookie(TEST_COOKIE, rand);
0891: setCookiePath(req, newprobe);
0892: res.addCookie(newprobe);
0893: sess.setAttribute(RAND_SESS_COOKIE_VALUE, rand);
0894: return true;
0895: }
0896: return false;
0897: }
0898:
0899: private Cookie getSecureSessionCookie(HttpServletRequest req,
0900: String sessionid) {
0901: Cookie[] cookies = req.getCookies();
0902: Cookie tmp;
0903: if (cookies != null) {
0904: for (int i = 0; i < cookies.length; i++) {
0905: tmp = cookies[i];
0906: if (tmp.getName().equals(
0907: SECURE_SESS_COOKIE
0908: + MD5Utils.hex_md5(sessionid)))
0909: return tmp;
0910: }
0911: }
0912: return null;
0913: }
0914:
0915: private void registerSession(HttpServletRequest req,
0916: HttpSession session) {
0917: if (session != null) {
0918: synchronized (TIMESTAMP_ID) {
0919: SimpleDateFormat sdf = new SimpleDateFormat(
0920: "yyyyMMddHHmmss");
0921: String timestamp = sdf.format(new Date());
0922: NumberFormat nf = NumberFormat.getInstance();
0923: nf.setMinimumIntegerDigits(3);
0924:
0925: if (timestamp.equals(TIMESTAMP_ID)) {
0926: INC_ID++;
0927: } else {
0928: TIMESTAMP_ID = timestamp;
0929: INC_ID = 0;
0930: }
0931: if (INC_ID >= 1000) {
0932: LOG.warn("*** More than 999 connects/sec! ***");
0933: }
0934: String sessid = session.getId();
0935: String mach = "";
0936: if (sessid.lastIndexOf(".") > 0) {
0937: mach = sessid.substring(sessid.lastIndexOf("."));
0938: }
0939: session.setAttribute(VISIT_ID, TIMESTAMP_ID + "-"
0940: + nf.format(INC_ID) + mach);
0941: }
0942: StringBuffer logbuff = new StringBuffer();
0943: logbuff.append(session.getAttribute(VISIT_ID) + "|"
0944: + session.getId() + "|");
0945: logbuff.append(getServerName(req) + "|"
0946: + req.getRemoteAddr() + "|"
0947: + req.getHeader("user-agent") + "|");
0948: if (req.getHeader("referer") != null) {
0949: logbuff.append(req.getHeader("referer"));
0950: }
0951: logbuff.append("|");
0952: if (req.getHeader("accept-language") != null) {
0953: logbuff.append(req.getHeader("accept-language"));
0954: }
0955: LOGGER_VISIT.warn(logbuff.toString());
0956: SessionAdmin.getInstance().registerSession(session,
0957: getServerName(req), req.getRemoteAddr());
0958: }
0959: }
0960:
0961: public void init(ServletConfig config) throws ServletException {
0962: super .init(config);
0963:
0964: ServletContext ctx = config.getServletContext();
0965: LOG.debug("*** Servlet container is '" + ctx.getServerInfo()
0966: + "'");
0967: int major = ctx.getMajorVersion();
0968: int minor = ctx.getMinorVersion();
0969: if ((major == 2 && minor >= 3) || (major > 2)) {
0970: LOG
0971: .warn("*** Servlet container with support for Servlet API "
0972: + major + "." + minor + " detected");
0973: } else {
0974: throw new ServletException(
0975: "*** Can't detect servlet container with support for Servlet API 2.3 or higher");
0976: }
0977:
0978: Properties properties = new Properties(System.getProperties());
0979:
0980: String commonpropfilename = config
0981: .getInitParameter("servlet.commonpropfile");
0982: if (commonpropfilename != null) {
0983: if (!commonpropfilename.startsWith("/")) {
0984: commonpropfile = ResourceUtil
0985: .getFileResourceFromDocroot(commonpropfilename);
0986: } else {
0987: commonpropfile = ResourceUtil.getFileResource("file://"
0988: + commonpropfilename);
0989: }
0990: // Load on first request
0991: common_mtime = loadPropertyfile(properties, commonpropfile);
0992: }
0993:
0994: String servletpropfilename = config
0995: .getInitParameter("servlet.propfile");
0996: if (servletpropfilename != null) {
0997: if (!servletpropfilename.startsWith("/")) {
0998: servletpropfile = ResourceUtil
0999: .getFileResourceFromDocroot(servletpropfilename);
1000: } else {
1001: servletpropfile = ResourceUtil
1002: .getFileResource("file://"
1003: + servletpropfilename);
1004: }
1005: }
1006:
1007: // Make sure configuration is available
1008: this .reloadServletConfig(servletpropfile, properties);
1009:
1010: initCookieSec();
1011: initExceptionConfigs();
1012: initServletEncoding();
1013: }
1014:
1015: protected boolean tryReloadProperties(PfixServletRequest preq)
1016: throws ServletException {
1017: if ((commonpropfile != null && commonpropfile.lastModified() > common_mtime)
1018: || (servletpropfile != null && servletpropfile
1019: .lastModified() > servlet_mtime)
1020: || (this .getServletManagerConfig() != null && this
1021: .getServletManagerConfig().needsReload())) {
1022:
1023: int currLoadIndex = configLoadIndex.incrementAndGet();
1024:
1025: LOG.warn("\n\n##############################\n"
1026: + "#### Reloading properties ####\n"
1027: + "##############################\n");
1028: Properties properties = new Properties(System
1029: .getProperties());
1030:
1031: if (commonpropfile != null) {
1032: common_mtime = loadPropertyfile(properties,
1033: commonpropfile);
1034: }
1035: servlet_mtime = servletpropfile.lastModified();
1036: this .reloadServletConfig(servletpropfile, properties);
1037: this .getServletManagerConfig().getProperties().setProperty(
1038: PROP_LOADINDEX, String.valueOf(currLoadIndex));
1039:
1040: initCookieSec();
1041: return true;
1042: } else {
1043: return false;
1044: }
1045:
1046: }
1047:
1048: /**
1049: * This is only for broken clients who suddenly stop supplying cookies in the middle of a
1050: * session. Windows XP internet configuration wizard (which speaks http) seems to be an example of a
1051: * client that remembers the cookies over the first request/relocate/relocate cycle, but never sends a
1052: * cookie again on the next request.
1053: * You are strongly advised to NOT set the corresponding property to true, unless you deal with broken software.
1054: */
1055: private void initCookieSec() {
1056: String csec = this .getServletManagerConfig().getProperties()
1057: .getProperty(PROP_COOKIE_SEC_NOT_ENFORCED);
1058: if (csec != null && csec.equals("true")) {
1059: cookie_security_not_enforced = true;
1060: } else {
1061: cookie_security_not_enforced = false;
1062: }
1063: }
1064:
1065: private long loadPropertyfile(Properties props,
1066: FileResource propfile) throws ServletException {
1067: long mtime;
1068: try {
1069: mtime = propfile.lastModified();
1070: XMLPropertiesUtil
1071: .loadPropertiesFromXMLFile(propfile, props);
1072: } catch (FileNotFoundException e) {
1073: throw new ServletException("*** [" + propfile.getName()
1074: + "] Not found: " + e.toString());
1075: } catch (IOException e) {
1076: throw new ServletException("*** [" + propfile.getName()
1077: + "] IO-error: " + e.toString());
1078: } catch (SAXException e) {
1079: throw new ServletException("*** [" + propfile.getName()
1080: + "] Parsing-error: " + e.toString());
1081: }
1082: return mtime;
1083: }
1084:
1085: private void callProcess(PfixServletRequest preq,
1086: HttpServletRequest req, HttpServletResponse res)
1087: throws ServletException, IOException {
1088: try {
1089: if (!FactoryInitServlet.isConfigured()
1090: && !FactoryInitUtil.isInitialized()) {
1091: FactoryInitException initEx = FactoryInitServlet
1092: .getInitException();
1093: if (initEx != null) {
1094: initEx = initEx.copy();
1095: initEx.fillInStackTrace();
1096: throw initEx;
1097: } else
1098: throw new IllegalStateException(
1099: "Factories aren't initialized yet.");
1100: }
1101: res.setContentType(DEF_CONTENT_TYPE);
1102: process(preq, res);
1103: } catch (Throwable e) {
1104: LOG.error("Exception in process", e);
1105: ExceptionConfig exconf = getExceptionConfigForThrowable(e
1106: .getClass());
1107: if (exconf != null && exconf.getProcessor() != null) {
1108: if (preq.getLastException() == null) {
1109: ExceptionProcessor eproc = exconf.getProcessor();
1110: eproc.processException(e, exconf, preq,
1111: getServletConfig().getServletContext(),
1112: req, res, this .getServletManagerConfig()
1113: .getProperties());
1114: }
1115: }
1116: if (!res.isCommitted())
1117: throw new ServletException("Exception in process.", e);
1118: }
1119: }
1120:
1121: /**
1122: *
1123: * @return null if no processor is responsible for the passed throwable
1124: * @throws ServletException
1125: * @throws ClassNotFoundException
1126: */
1127: private ExceptionConfig getExceptionConfigForThrowable(
1128: Class<? extends Throwable> clazz) throws ServletException {
1129: ExceptionConfig exConf = null;
1130: if (clazz != null) {
1131: exConf = exceptionConfigs.get(clazz);
1132: if (exConf == null)
1133: exConf = getExceptionConfigForThrowable(clazz
1134: .getSuperclass().asSubclass(Throwable.class));
1135: }
1136: return exConf;
1137: }
1138:
1139: /**
1140: * This method uses all properties prefixed with 'exception' to build ExceptionConfig
1141: * objects, which are then stored in the exConfig-Map, keyed by the type attribute
1142: * of the ExceptionConfig.
1143: * Syntax of key: "exception" '.' JAVATYPE '.' ("forward"|"page"|"processor")
1144: * @exception ServletException if the exception configuration defined in the properties
1145: * is somehow invalid
1146: */
1147: private void initExceptionConfigs() throws ServletException {
1148: Map<String, ExceptionConfig> tmpExConf = new HashMap<String, ExceptionConfig>();
1149: Properties properties = this .getServletManagerConfig()
1150: .getProperties();
1151: Enumeration<?> props = properties.propertyNames();
1152: while (props.hasMoreElements()) {
1153: String propName = (String) props.nextElement();
1154:
1155: if (propName.startsWith(PROP_EXCEPTION)) {
1156: String propValue = properties.getProperty(propName);
1157:
1158: String type = null;
1159: String attrName = null;
1160: IndexOutOfBoundsException ioobex = null;
1161: try {
1162: int dot1 = propName.indexOf('.');
1163: int dot2 = propName.lastIndexOf('.');
1164: type = propName.substring(dot1 + 1, dot2);
1165: attrName = propName.substring(dot2 + 1);
1166: } catch (IndexOutOfBoundsException e) {
1167: // logging is done below
1168: ioobex = e;
1169: }
1170: if (type == null || attrName == null || "".equals(type)
1171: || "".equals(attrName)) {
1172: LOG
1173: .debug(
1174: "Could not parse property key \""
1175: + propName
1176: + "\" into three non-empty parts separated by '.'",
1177: ioobex);
1178: }
1179:
1180: ExceptionConfig exConf = (ExceptionConfig) tmpExConf
1181: .get(type);
1182:
1183: LOG.debug("Property found for exception processing: "
1184: + propName + "=" + propValue);
1185:
1186: if (exConf == null) {
1187: exConf = new ExceptionConfig();
1188: exConf.setType(type);
1189: tmpExConf.put(type, exConf);
1190: }
1191:
1192: try {
1193: if ("forward".equals(attrName)) {
1194: exConf.setForward(Boolean.valueOf(propValue)
1195: .booleanValue());
1196: } else if ("page".equals(attrName)) {
1197: exConf.setPage(propValue);
1198: } else if ("processor".equals(attrName)) {
1199: Class<?> procClass = Class.forName(propValue);
1200: ExceptionProcessor exProc = (ExceptionProcessor) procClass
1201: .newInstance();
1202: exConf.setProcessor(exProc);
1203: }
1204: } catch (ClassCastException ex) {
1205: throw new ServletException(
1206: "INVALID CONF: Class "
1207: + propValue
1208: + " is not an instance of 'ExceptionProcessor'");
1209: } catch (IllegalAccessException ex) {
1210: throw new ServletException(
1211: "INVALID CONF: Can't create instance of class "
1212: + propName, ex);
1213: } catch (SecurityException ex) {
1214: throw new ServletException(
1215: "INVALID CONF: Can't create instance of class "
1216: + propName, ex);
1217: } catch (ClassNotFoundException ex) {
1218: throw new ServletException(
1219: "INVALID CONF: Can't create instance of class "
1220: + propName, ex);
1221: } catch (InstantiationException ex) {
1222: throw new ServletException(
1223: "INVALID CONF: Can't create instance of class "
1224: + propName, ex);
1225: }
1226: }
1227: }
1228:
1229: LOG
1230: .debug("Finished reading properties for Exception configuration! \n"
1231: + tmpExConf);
1232:
1233: exceptionConfigs.clear();
1234:
1235: // validate the ExceptionConfig-instances and save them, keyed by their type-attribute
1236: for (Iterator<ExceptionConfig> values = tmpExConf.values()
1237: .iterator(); values.hasNext();) {
1238: ExceptionConfig exConfig = values.next();
1239: if (exConfig.validate() == false)
1240: throw new ServletException(
1241: "INVALID ExceptionConfig: \n" + exConfig);
1242: else {
1243: try {
1244: Class<?> clazz = Class.forName(exConfig.getType());
1245: exceptionConfigs.put(clazz
1246: .asSubclass(Throwable.class), exConfig);
1247: } catch (ClassNotFoundException x) {
1248: throw new ServletException(
1249: "Can't find exception class: "
1250: + exConfig.getType());
1251: }
1252: }
1253: }
1254:
1255: if (LOG.isDebugEnabled()) {
1256: LOG.debug("Complete ExceptionConfig is:");
1257: LOG.debug("\n" + exceptionConfigs);
1258: }
1259: }
1260:
1261: /**
1262: * Sets the servlet's encoding, which is used as character encoding for decoding/encoding
1263: * requests/responses. Be aware that this setting only applies to the appropriate Readers,
1264: * Writers and body request parameters. It has no effect on the byte streams. The URI
1265: * encoding (which is set on Tomcat connector level and can't be changed here) is set always
1266: * be the same as the body encoding.
1267: */
1268: private void initServletEncoding() {
1269: //Try to get servlet encoding from properties:
1270: String encoding = this .getServletManagerConfig()
1271: .getProperties().getProperty(SERVLET_ENCODING);
1272: if (encoding == null || encoding.trim().equals(""))
1273: LOG.warn("No servlet encoding property set");
1274: else if (!Charset.isSupported(encoding))
1275: LOG.error("Servlet encoding '" + encoding
1276: + "' is not supported.");
1277: else
1278: servletEncoding = encoding;
1279:
1280: //Try to get servlet encoding from init parameters:
1281: if (servletEncoding == null) {
1282: encoding = getServletConfig().getInitParameter(
1283: SERVLET_ENCODING);
1284: if (encoding == null || encoding.trim().equals(""))
1285: LOG.warn("No servlet encoding init parameter set");
1286: else if (!Charset.isSupported(encoding))
1287: LOG.error("Servlet encoding '" + encoding
1288: + "' is not supported.");
1289: else
1290: servletEncoding = encoding;
1291: }
1292: //Use default servlet encoding:
1293: if (servletEncoding == null) {
1294: servletEncoding = DEFAULT_ENCODING;
1295: LOG.warn("Using default servlet encoding: "
1296: + DEFAULT_ENCODING);
1297: }
1298:
1299: LOG.debug("Servlet encoding was set to '" + servletEncoding
1300: + "'.");
1301: }
1302:
1303: protected abstract void process(PfixServletRequest preq,
1304: HttpServletResponse res) throws Exception;
1305:
1306: public static final int HTTP_PORT = 80;
1307: public static final int HTTPS_PORT = 443;
1308:
1309: public static boolean isDefault(String scheme, int port) {
1310: if (scheme.equals("http") && port == HTTP_PORT) {
1311: return true;
1312: } else if (scheme.equals("https") && port == HTTPS_PORT) {
1313: return true;
1314: } else {
1315: return false;
1316: }
1317: }
1318:
1319: private class SortableCookie implements Comparable<SortableCookie> {
1320: public final Cookie cookie;
1321: public final long lasttouch;
1322:
1323: public final int compareTo(final SortableCookie in) {
1324: if (in.lasttouch > lasttouch)
1325: return -1;
1326: if (in.lasttouch < lasttouch)
1327: return 1;
1328: else
1329: return 0;
1330: }
1331:
1332: public SortableCookie(Cookie cookie, long lasttouch) {
1333: this .cookie = cookie;
1334: this .lasttouch = lasttouch;
1335: assert (cookie != null) : "cookie argument must not be null";
1336: assert (lasttouch > 0) : "lasttouch argument must be > 0";
1337: }
1338: }
1339:
1340: private void cleanupCookies(HttpServletRequest req,
1341: HttpServletResponse res, Cookie cookie) {
1342: HttpSession session = req.getSession(false);
1343: assert (session != null) : "session can't be null here...";
1344:
1345: Long timeout = System.currentTimeMillis()
1346: - (1000 * session.getMaxInactiveInterval());
1347: assert (timeout > 0) : "timeout can't be negative...";
1348:
1349: Cookie[] cookies = req.getCookies();
1350: if (cookies != null && cookies.length > 0) {
1351: TreeSet<SortableCookie> cset = new TreeSet<SortableCookie>();
1352:
1353: for (int i = 0; i < cookies.length; i++) {
1354: Cookie tmp = cookies[i];
1355: if (tmp.getName().startsWith(SECURE_SESS_COOKIE)
1356: && (cookie == null || !tmp.getName().equals(
1357: cookie.getName()))) {
1358: String value = tmp.getValue();
1359: String stamp = value.substring(0, value
1360: .indexOf(":"));
1361: try {
1362: long lasttouch = Long.parseLong(stamp);
1363: cset.add(new SortableCookie(tmp, lasttouch));
1364: LOG.debug("~~~ Adding cookie " + lasttouch
1365: + "->" + tmp.getName());
1366: } catch (NumberFormatException e) {
1367: setCookiePath(req, tmp);
1368: tmp.setMaxAge(0);
1369: tmp.setSecure(true);
1370: res.addCookie(tmp);
1371: }
1372: }
1373: }
1374: int count = 0;
1375: int length = cset.size();
1376: for (Iterator<SortableCookie> iter = cset.iterator(); iter
1377: .hasNext(); count++) {
1378: SortableCookie current = iter.next();
1379: Cookie curr_cookie = current.cookie;
1380: long curr_lasttouch = current.lasttouch;
1381:
1382: LOG
1383: .debug("--- Checking cookie " + count + "->"
1384: + curr_lasttouch + "->"
1385: + curr_cookie.getName());
1386:
1387: if (count <= (length - MAX_PARALLEL_SEC_SESSIONS)) {
1388: LOG
1389: .debug(" -> removing cookie because number of secure session cookies too high");
1390: curr_cookie.setMaxAge(0);
1391: curr_cookie.setSecure(true);
1392: setCookiePath(req, curr_cookie);
1393: res.addCookie(curr_cookie);
1394: } else if (curr_lasttouch < timeout) {
1395: // FIXME We shoudln't need to check for "old" cookies here, because the
1396: // lifetime of a cookie should ideally be set to the max. inactive time of the
1397: // session, so the browser would stop sending old cookies by itself. But I'm not
1398: // entirely sure if we can really depend on having the time set right on all
1399: // clients... What happens if the client clock is set half an hour too early?
1400: // Will the browser stop sending the cookie half an hour before the session is
1401: // really invalidated? As we need the timestamp info anyway to sort the cookies,
1402: // we can also do the timeout removing here.
1403: LOG
1404: .debug(" -> removing cookie because timestamp too old");
1405: curr_cookie.setMaxAge(0);
1406: curr_cookie.setSecure(true);
1407: setCookiePath(req, curr_cookie);
1408: res.addCookie(curr_cookie);
1409: } else {
1410: break;
1411: }
1412: }
1413: }
1414: }
1415:
1416: private void setCookiePath(HttpServletRequest req, Cookie cookie) {
1417: if (req.getContextPath().length() > 0) {
1418: cookie.setPath(req.getContextPath());
1419: } else {
1420: cookie.setPath("/");
1421: }
1422: }
1423:
1424: }// ServletManager
|