Java tutorial
/******************************************************************************* * Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved. * * This file is part of the OpenWGA server platform. * * OpenWGA is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * In addition, a special exception is granted by the copyright holders * of OpenWGA called "OpenWGA plugin exception". You should have received * a copy of this exception along with OpenWGA in file COPYING. * If not, see <http://www.openwga.com/gpl-plugin-exception>. * * OpenWGA 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenWGA in file COPYING. * If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package de.innovationgate.wgpublisher.filter; import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.WeakHashMap; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpSession; import org.apache.commons.lang.ArrayUtils; import de.innovationgate.utils.WGUtils; import de.innovationgate.utils.URLBuilder; import de.innovationgate.webgate.api.WGFactory; import de.innovationgate.wgpublisher.WGACore; import de.innovationgate.wgpublisher.WGCookie; import de.innovationgate.wgpublisher.WGPDeployer; import de.innovationgate.wgpublisher.log.WGALoggerWrapper; import de.innovationgate.wgpublisher.log.WGARequestInformation; import de.innovationgate.wgpublisher.lucene.LuceneManager; import de.innovationgate.wgpublisher.problems.DatabaseScope; import de.innovationgate.wgpublisher.problems.GlobalScope; import de.innovationgate.wgpublisher.problems.LongRequestProblemType; import de.innovationgate.wgpublisher.problems.Problem; import de.innovationgate.wgpublisher.problems.ProblemOccasion; import de.innovationgate.wgpublisher.problems.ProblemScope; import de.innovationgate.wgpublisher.problems.ProblemSeverity; import de.innovationgate.wgpublisher.problems.ProblemType; import de.innovationgate.wgpublisher.sessions.AbstractWGAHttpSessionManager; import de.innovationgate.wgpublisher.sessions.WGAHttpSession; public class WGAFilter implements Filter { private static final String COOKIE_NAME_WGSESSION = "WGSESSIONID"; private static final String COOKIE_NAME_WGSECURESESSION = "WGSECURESESSIONID"; private static final String COOKIE_NAME_LBROUTE = "WGLBROUTE"; public static final String REQATTRIB_ORIGINAL_URI = WGAFilter.class.getName() + ".originalRequestURI"; public static final String REQATTRIB_ORIGINAL_URL = WGAFilter.class.getName() + ".originalRequestURL"; public static final String REQATTRIB_ORIGINAL_QUERYSTRING = WGAFilter.class.getName() + ".originalQueryString"; public static final String REQATTRIB_REQUEST_WRAPPER = WGAFilter.class.getName() + ".requestWrapper"; public static final String REQATTRIB_RESPONSE_WRAPPER = WGAFilter.class.getName() + ".responseWRapper"; public static final String SESATTRIB_MOCK_CERT = WGAFilter.class.getName() + ".mockCert"; public static final long REQUEST_LENGTH_NOTIFICATION_THRESHOLD = 1000 * 10; private WGACore _core; private WGAFilterChain _wgaFilterChain; private ServletContext _servletContext; private Map<ServletRequest, WGARequestInformation> _currentRequests = new WeakHashMap<ServletRequest, WGARequestInformation>(); private ReadWriteLock _holdRequestsLock = new ReentrantReadWriteLock(); public static class FilterRequestOccasion implements ProblemOccasion { @Override public ProblemScope getDefaultScope() { return GlobalScope.INSTANCE; } @Override public Class<? extends ProblemType> getDefaultType() { return LongRequestProblemType.class; } @Override public Class<?> getDefaultRefClass() { return WGAFilter.class; } @Override public boolean isClearedAutomatically() { return false; } @Override public int hashCode() { return getClass().hashCode(); } @Override public boolean equals(Object obj) { return (obj instanceof FilterRequestOccasion); } } /** * wrapper for HttpServletResponse * - ignores all calls to setCharacterEncoding * - replace parameter charset=<XY> during setContentType with <wrappedResponse>.getCharacterEncoding() * Therefore the output encoding can be final set on the original response. * */ public class FinalCharacterEncodingResponseWrapper extends HttpServletResponseWrapper { public static final String SYSPROP_ENCODING_ON_CONTENTTYPE = "de.innovationgate.wga.encoding.oncontenttype"; private boolean _encodingOnContentType = false; private int _statusCode = HttpServletResponse.SC_OK; private String _statusMessage; public FinalCharacterEncodingResponseWrapper(RequestWrapper reqWrapper, HttpServletResponse arg0) { super(arg0); _encodingOnContentType = Boolean.getBoolean(SYSPROP_ENCODING_ON_CONTENTTYPE); } public void setCharacterEncoding(String arg0) { // ignore } public void setContentType(String arg0) { String contentType = arg0; if (contentType != null) { if (contentType.indexOf(";") != -1) { List<String> parameters = WGUtils.deserializeCollection(contentType, ";"); boolean charsetParamSet = false; for (int i = 1; i < parameters.size(); i++) { String parameter = (String) parameters.get(i); parameter = parameter.trim(); if (parameter.toLowerCase().startsWith("charset")) { parameters.set(i, "charset=" + getResponse().getCharacterEncoding()); charsetParamSet = true; } } if (_encodingOnContentType && !charsetParamSet && contentType.toLowerCase().trim().startsWith("text/")) { // this is for a websphere bug // if character encoding is set before content type and content type // is given without charset websphere resets the charset to platform default // therefore we ensure here that all of our content types have the correct // charset parameter parameters.add("charset=" + getResponse().getCharacterEncoding()); } contentType = WGUtils.serializeCollection(parameters, ";"); } else if (_encodingOnContentType && contentType.toLowerCase().trim().startsWith("text/")) { // see above about websphere bug contentType += ";charset=" + getResponse().getCharacterEncoding(); } } getResponse().setContentType(contentType); } @Override public void sendError(int sc, String msg) throws IOException { super.sendError(sc, msg); _statusCode = sc; _statusMessage = msg; } @Override public void sendError(int sc) throws IOException { super.sendError(sc); _statusCode = sc; _statusMessage = null; } @SuppressWarnings("deprecation") @Override public void setStatus(int sc, String sm) { super.setStatus(sc, sm); _statusCode = sc; _statusMessage = sm; } @Override public void setStatus(int sc) { super.setStatus(sc); _statusCode = sc; _statusMessage = null; } public int getStatusCode() { return _statusCode; } public String getStatusMessage() { return _statusMessage; } @Override public void sendRedirect(String location) throws IOException { super.sendRedirect(location); setStatus(SC_MOVED_TEMPORARILY); } // @Override // public String encodeRedirectUrl(String url) { // return encodeRedirectURL(url); // } // // // @Override // public String encodeUrl(String url) { // return encodeRedirectURL(url); // } // // @Override // public String encodeURL(String url) { // if (_core.getHttpSessionManager() == null) { // return super.encodeURL(url); // } // // HttpSession session = _reqWrapper.getSession(false); // if (session != null && session.isNew()) { // return url + ";jsessionid=" + session.getId(); // } // return url; // } // // // @Override // public String encodeRedirectURL(String url) { // if (_core.getHttpSessionManager() == null) { // return super.encodeRedirectUrl(url); // } // // return encodeURL(url); // } } /** * a wrapper for WGA requests * - supports decoding of url parameters (parameters of the querystring) in the correct encoding * - supports setting defaults for url parameters if they are empty (not submitted) * */ public class RequestWrapper extends HttpServletRequestWrapper { private String _forwardedProtocol; private Map<String, String[]> _parameters = new HashMap<>(); private HttpServletRequest _wrappedRequest; private HttpServletResponse _response; private WGAHttpSession _wgaHttpSession; public RequestWrapper(HttpServletRequest request, HttpServletResponse reponse) { super(request); _wrappedRequest = request; _response = reponse; _forwardedProtocol = request.getHeader("X-Forwarded-Proto"); String queryString = request.getQueryString(); // decode query string parameters with wga character encoding if (queryString != null) { StringTokenizer keyValuePairs = new StringTokenizer(queryString, "&"); while (keyValuePairs.hasMoreTokens()) { String keyValuePair = keyValuePairs.nextToken(); String key = null; String value = null; int pos = keyValuePair.indexOf("="); if (pos != -1) { key = keyValuePair.substring(0, pos); if (pos < keyValuePair.length() - 1) { value = keyValuePair.substring(pos + 1); } else { value = ""; } try { String keyDecoded = _core.getURLEncoder().decode(key); String valueDecoded = _core.getURLEncoder().decode(value); String values[] = (String[]) _parameters.get(keyDecoded); values = (String[]) ArrayUtils.add(values, valueDecoded); _parameters.put(keyDecoded, values); } catch (Exception e) { WGFactory.getLogger().warn("Unable to decode request parameter '" + key + "'.", e); } } } } } @Override public String getProtocol() { if (_forwardedProtocol != null) return _forwardedProtocol + "/1.0"; else return _wrappedRequest.getProtocol(); } @Override public String getScheme() { if (_forwardedProtocol != null) return _forwardedProtocol; else return _wrappedRequest.getScheme(); } @Override public int getServerPort() { int port = _wrappedRequest.getServerPort(); String protocol = _wrappedRequest.getScheme().toLowerCase(); if (_forwardedProtocol != null && URLBuilder.isDefaultPortForProtocol(port, protocol)) { // we have default port in original request: set NEW default return URLBuilder.getDefaultPortForProtocol(_forwardedProtocol.toLowerCase()); } return port; } @Override public StringBuffer getRequestURL() { StringBuffer currentURL = _wrappedRequest.getRequestURL(); if (_forwardedProtocol != null) { int i = currentURL.indexOf("://"); if (i > 0) return new StringBuffer(_forwardedProtocol + "://" + currentURL.substring(i + 3)); } return new StringBuffer(currentURL); } public String getParameter(String name) { copyNoneExistingParams(_wrappedRequest.getParameterMap()); String values[] = (String[]) _parameters.get(name); if (values == null || values.length == 0) { return null; } else { return values[0]; } } public Map<String, String[]> getParameterMap() { copyNoneExistingParams(_wrappedRequest.getParameterMap()); return new HashMap<String, String[]>(_parameters); } public String[] getParameterValues(String name) { copyNoneExistingParams(_wrappedRequest.getParameterMap()); return (String[]) _parameters.get(name); } public Enumeration<String> getParameterNames() { copyNoneExistingParams(_wrappedRequest.getParameterMap()); return Collections.enumeration(_parameters.keySet()); } public void setParameterIfEmpty(String name, String value) { String[] values = getParameterValues(name); if (values == null || values.length == 0 || (values.length == 1 && values[0].equals(""))) { String valueArr[] = new String[1]; valueArr[0] = value; _parameters.put(name, valueArr); } } public void setParameterIfEmpty(String name, String[] values) { String[] existingValues = getParameterValues(name); if (existingValues == null || existingValues.length == 0 || (existingValues.length == 1 && existingValues[0].equals(""))) { _parameters.put(name, values); } } private void copyNoneExistingParams(Map<String, String[]> from) { // add all parameters from the request to local map, which where not already parsed from querystring or added via setParameterIfEmpty // necessary for jsp-param parsing HashMap<String, String[]> temp = new HashMap<>(from); Iterator<String> localParams = _parameters.keySet().iterator(); while (localParams.hasNext()) { String localParam = (String) localParams.next(); temp.remove(localParam); } _parameters.putAll(temp); } @Override public HttpSession getSession() { return getSession(true); } @Override public HttpSession getSession(boolean create) { if (_core.getHttpSessionManager() == null) { return super.getSession(create); } if (_wgaHttpSession == null) { AbstractWGAHttpSessionManager manager = _core.getHttpSessionManager(); String requestedSessionID = getRequestedSessionId(); getOrCreateManagedHttpSession(create, manager, requestedSessionID); } return _wgaHttpSession; } private void getOrCreateManagedHttpSession(boolean create, AbstractWGAHttpSessionManager manager, String requestedSessionID) { // Try to fetch existing session for given ID if (requestedSessionID != null) { _wgaHttpSession = manager.getSession(requestedSessionID); if (_wgaHttpSession != null) { _wgaHttpSession.setNew(false); // check if session id has changed - might be the case on replicated sticky sessions where jvmRoute changed due to a cluster failover if (!_wgaHttpSession.getId().equals(requestedSessionID)) { if (manager.isDebug()) { _core.getLog() .info("session manager has changed session id for existing session '" + requestedSessionID + "' -> '" + _wgaHttpSession.getId() + "' - updating cookie information"); } addSessionCookie(_wgaHttpSession.getId()); } return; } } // Creating new session, cancel if not allowed if (!create) { return; } if (manager.isDebug()) { _core.getLog().info("no session id yet - creating new session"); } _wgaHttpSession = manager.createSession(); // For a secure request check if an unsecure session is available and push existing session data to new session if (_wrappedRequest.isSecure()) { String requestedUnSecureSessionID = getRequestedUnsecureSessionId(); if (requestedUnSecureSessionID != null) { WGAHttpSession unsecureSession = manager.getSession(requestedUnSecureSessionID); if (unsecureSession != null) { if (manager.isDebug()) { _core.getLog().info("existing unsecure session found with id '" + requestedUnSecureSessionID + "' - pushing data to new secure session"); } unsecureSession.pushData(_wgaHttpSession); } } } // Write session cookie addSessionCookie(_wgaHttpSession.getId()); } @Override public boolean isRequestedSessionIdValid() { if (_core.getHttpSessionManager() == null) { return _wrappedRequest.isRequestedSessionIdValid(); } WGAHttpSession session = _core.getHttpSessionManager().getSession(getRequestedSessionId()); return session != null; } @Override public String getRequestedSessionId() { if (_core.getHttpSessionManager() == null) { return _wrappedRequest.getRequestedSessionId(); } Cookie[] cookies = _wrappedRequest.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(getSessionCookieName())) { return cookie.getValue(); } } } return null; } private String getSessionCookieName() { if (_wrappedRequest.isSecure()) { return COOKIE_NAME_WGSECURESESSION; } else { return COOKIE_NAME_WGSESSION; } } private String getRequestedUnsecureSessionId() { Cookie[] cookies = _wrappedRequest.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(COOKIE_NAME_WGSESSION)) { return cookie.getValue(); } } } return null; } @Override public boolean isRequestedSessionIdFromCookie() { if (_core.getHttpSessionManager() == null) { return _wrappedRequest.isRequestedSessionIdFromCookie(); } return true; } @Override public boolean isRequestedSessionIdFromURL() { if (_core.getHttpSessionManager() == null) { return _wrappedRequest.isRequestedSessionIdFromURL(); } return false; } @Override public boolean isRequestedSessionIdFromUrl() { return isRequestedSessionIdFromURL(); } private void addSessionCookie(String sessionId) { if (_wrappedRequest.getAttribute("SESSIONCOOKIE_SET") == null || !(Boolean) _wrappedRequest.getAttribute("SESSIONCOOKIE_SET")) { WGCookie sessionCookie = new WGCookie(getSessionCookieName(), sessionId); sessionCookie.setPath("/"); sessionCookie.setMaxAge(-1); sessionCookie.setSecure(_wrappedRequest.isSecure()); sessionCookie.addCookieHeader(_response); if (_core.getHttpSessionManager() != null && _core.getHttpSessionManager().isDebug()) { _core.getLog() .info("adding '" + getSessionCookieName() + "' " + sessionCookie.getValue() + " - cookie.path " + sessionCookie.getPath() + " - secure " + sessionCookie.getSecure()); } _wrappedRequest.setAttribute("SESSIONCOOKIE_SET", true); } } public HttpServletRequest getWrappedRequest() { return _wrappedRequest; } } /* (Kein Javadoc) * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) */ public void init(FilterConfig arg0) throws ServletException { _servletContext = arg0.getServletContext(); _core = WGACore.retrieve(_servletContext); if (_core != null) { _core.setFilter(this); } setupFilterChain(); } public void setupFilterChain() { _holdRequestsLock.writeLock().lock(); try { if (_wgaFilterChain != null) { _core.getLog().info("Shutting down filter chain"); _wgaFilterChain.destroy(); } _core.getLog().info("Initializing filter chain"); _wgaFilterChain = new WGAFilterChain(_core, _servletContext); } finally { _holdRequestsLock.writeLock().unlock(); } } /* (Kein Javadoc) * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { WGARequestInformation info = new WGARequestInformation(); try { info.setStartTime(System.currentTimeMillis()); info.setThread(Thread.currentThread()); request.setAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME, info); _wgaFilterChain.init(request, chain); _currentRequests.put(request, info); // F000037B2 if (_core.getCharacterEncoding() != null) { request.setCharacterEncoding(_core.getCharacterEncoding()); // B000041DA response.setCharacterEncoding(_core.getCharacterEncoding()); } // add/ delete jvmRoute String lbRoute = _core.getClusterService().getLBRoute(); if (lbRoute != null && !lbRoute.trim().equals("")) { WGCookie jvmRouteCookie = new WGCookie(COOKIE_NAME_LBROUTE, lbRoute); jvmRouteCookie.setPath("/"); jvmRouteCookie.setMaxAge(-1); jvmRouteCookie.addCookieHeader((HttpServletResponse) response); } else { Cookie cookie = getCookie((HttpServletRequest) request, COOKIE_NAME_LBROUTE); if (cookie != null) { WGCookie jvmRouteCookie = WGCookie.from(cookie); jvmRouteCookie.setMaxAge(0); jvmRouteCookie.addCookieHeader((HttpServletResponse) response); } } RequestWrapper wrappedRequest = createRequestWrapper(response, (HttpServletRequest) request); /* * #00005078: don't store original URL. Store converted URL instead. * Will be used in WGA.urlBuilder() and other sources */ request.setAttribute(REQATTRIB_ORIGINAL_URL, wrappedRequest.getRequestURL().toString()); request.setAttribute(REQATTRIB_ORIGINAL_URI, wrappedRequest.getRequestURI()); request.setAttribute(REQATTRIB_ORIGINAL_QUERYSTRING, wrappedRequest.getQueryString()); FinalCharacterEncodingResponseWrapper wrappedResponse = createResponseWrapper(response, wrappedRequest); _holdRequestsLock.readLock().lock(); try { _wgaFilterChain.doFilter(wrappedRequest, wrappedResponse); } finally { _holdRequestsLock.readLock().unlock(); } info.setStatusCode(wrappedResponse.getStatusCode()); info.setStatusMessage(wrappedResponse.getStatusMessage()); AbstractWGAHttpSessionManager sessionManager = _core.getHttpSessionManager(); if (sessionManager != null) { sessionManager.requestFinished(wrappedRequest, wrappedResponse); } } catch (ServletException e) { info.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); info.setStatusMessage(e.getMessage()); _core.getLog().error("Internal Server Error.", e); throw e; } finally { info.setEndTime(System.currentTimeMillis()); try { WGALoggerWrapper logger = _core.getAccessLogger(); if (logger != null) { logger.logRequest(request); } } catch (Exception e) { _core.getLog().error("Unable to log request.", e); } WGFactory.getInstance().closeSessions(); if (!Boolean.TRUE.equals(request.getAttribute(WGPDeployer.REQATTRIB_TML_DEPLOYED)) && info.getEndTime() > info.getStartTime() + REQUEST_LENGTH_NOTIFICATION_THRESHOLD) { String uri = ((HttpServletRequest) request).getRequestURI(); Problem.Vars vars = Problem.var("reqinfo", info) .var("completeurl", ((HttpServletRequest) request).getRequestURL().toString()) .var("host", ((HttpServletRequest) request).getServerName()).var("uri", uri); String problemKey = "requestProblem.longRequest#" + uri; if (info.getDatabase() != null) { _core.getProblemRegistry() .addProblem(Problem.create(new FilterRequestOccasion(), new DatabaseScope(info.getDatabase().getDbReference()), problemKey, ProblemSeverity.LOW, vars)); } else { _core.getProblemRegistry().addProblem( Problem.create(new FilterRequestOccasion(), problemKey, ProblemSeverity.LOW, vars)); } } // close opened lucene-resultsets LuceneManager luceneManager = _core.getLuceneManager(); if (luceneManager != null) { luceneManager.closeOpenedResultSets(); } _currentRequests.remove(request); } } protected FinalCharacterEncodingResponseWrapper createResponseWrapper(ServletResponse response, RequestWrapper wrappedRequest) { FinalCharacterEncodingResponseWrapper responseWrapper = new FinalCharacterEncodingResponseWrapper( wrappedRequest, (HttpServletResponse) response); wrappedRequest.getWrappedRequest().setAttribute(REQATTRIB_RESPONSE_WRAPPER, responseWrapper); return responseWrapper; } protected RequestWrapper createRequestWrapper(ServletResponse response, HttpServletRequest httpReq) { RequestWrapper wrapper = new RequestWrapper(httpReq, (HttpServletResponse) response); httpReq.setAttribute(REQATTRIB_REQUEST_WRAPPER, wrapper); return wrapper; } /* (Kein Javadoc) * @see javax.servlet.Filter#destroy() */ public void destroy() { _wgaFilterChain.destroy(); } public Map<ServletRequest, WGARequestInformation> getCurrentRequests() { return _currentRequests; } private Cookie getCookie(HttpServletRequest request, String name) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(name)) { return cookie; } } } return null; } public void initFilterChain() { Executors.newSingleThreadExecutor().execute(new Runnable() { @Override public void run() { setupFilterChain(); } }); } }