You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by je...@apache.org on 2016/02/08 17:51:56 UTC
[08/32] incubator-geode git commit: GEODE-14: Integration of GemFire
Session Replication and Hibernate modules
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionCachingFilter.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionCachingFilter.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionCachingFilter.java
new file mode 100644
index 0000000..7490fdd
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionCachingFilter.java
@@ -0,0 +1,652 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.filter;
+
+import com.gemstone.gemfire.modules.session.internal.filter.GemfireHttpSession;
+import com.gemstone.gemfire.modules.session.internal.filter.GemfireSessionManager;
+import com.gemstone.gemfire.modules.session.internal.filter.SessionManager;
+import com.gemstone.gemfire.modules.session.internal.filter.util.ThreadLocalSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+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 java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.security.Principal;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Primary class which orchestrates everything. This is the class which gets
+ * configured in the web.xml.
+ */
+public class SessionCachingFilter implements Filter {
+
+ /**
+ * Logger instance
+ */
+ private static final Logger LOG =
+ LoggerFactory.getLogger(SessionCachingFilter.class.getName());
+
+ /**
+ * The filter configuration object we are associated with. If this value is
+ * null, this filter instance is not currently configured.
+ */
+ private FilterConfig filterConfig = null;
+
+ /**
+ * Some containers will want to instantiate multiple instances of this filter,
+ * but we only need one SessionManager
+ */
+ private static SessionManager manager = null;
+
+ /**
+ * Can be overridden during testing.
+ */
+ private static AtomicInteger started =
+ new AtomicInteger(
+ Integer.getInteger("gemfire.override.session.manager.count", 1));
+
+ private static int percentInactiveTimeTriggerRebuild =
+ Integer.getInteger("gemfire.session.inactive.trigger.rebuild", 80);
+
+ /**
+ * This latch ensures that at least one thread/instance has fired up the
+ * session manager before any other threads complete the init method.
+ */
+ private static CountDownLatch startingLatch = new CountDownLatch(1);
+
+ /**
+ * This request wrapper class extends the support class
+ * HttpServletRequestWrapper, which implements all the methods in the
+ * HttpServletRequest interface, as delegations to the wrapped request. You
+ * only need to override the methods that you need to change. You can get
+ * access to the wrapped request using the method getRequest()
+ */
+ public static class RequestWrapper extends HttpServletRequestWrapper {
+
+ private static final String URL_SESSION_IDENTIFIER = ";jsessionid=";
+
+ private ResponseWrapper response;
+
+ private boolean sessionFromCookie = false;
+
+ private boolean sessionFromURL = false;
+
+ private String requestedSessionId = null;
+
+ private GemfireHttpSession session = null;
+
+ private SessionManager manager;
+
+ private HttpServletRequest outerRequest = null;
+
+ /**
+ * Need to save this in case we need the original {@code RequestDispatcher}
+ */
+ private HttpServletRequest originalRequest;
+
+ public RequestWrapper(SessionManager manager,
+ HttpServletRequest request,
+ ResponseWrapper response) {
+
+ super(request);
+ this.response = response;
+ this.manager = manager;
+ this.originalRequest = request;
+
+ final Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (final Cookie cookie : cookies) {
+ if (cookie.getName().equalsIgnoreCase(
+ manager.getSessionCookieName()) &&
+ cookie.getValue().endsWith("-GF")) {
+ requestedSessionId = cookie.getValue();
+ sessionFromCookie = true;
+
+ LOG.debug("Cookie contains sessionId: {}",
+ requestedSessionId);
+ }
+ }
+ }
+
+ if (requestedSessionId == null) {
+ requestedSessionId = extractSessionId();
+ LOG.debug("Extracted sessionId from URL {}", requestedSessionId);
+ if (requestedSessionId != null) {
+ sessionFromURL = true;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public HttpSession getSession() {
+ return getSession(true);
+ }
+
+ /**
+ * Create our own sessions. TODO: Handle invalidated sessions
+ *
+ * @return a HttpSession
+ */
+ @Override
+ public HttpSession getSession(boolean create) {
+ if (session != null && session.isValid()) {
+ session.setIsNew(false);
+ session.updateAccessTime();
+ /*
+ * This is a massively gross hack. Currently, there is no way
+ * to actually update the last accessed time for a session, so
+ * what we do here is once we're into X% of the session's TTL
+ * we grab a new session from the container.
+ *
+ * (inactive * 1000) * (pct / 100) ==> (inactive * 10 * pct)
+ */
+ if (session.getLastAccessedTime() - session.getCreationTime() >
+ (session.getMaxInactiveInterval() * 10 * percentInactiveTimeTriggerRebuild)) {
+ HttpSession nativeSession = super.getSession();
+ session.failoverSession(nativeSession);
+ }
+ return session;
+ }
+
+ if (requestedSessionId != null) {
+ session = (GemfireHttpSession) manager.getSession(
+ requestedSessionId);
+ if (session != null) {
+ session.setIsNew(false);
+ // This means we've failed over to another node
+ if (session.getNativeSession() == null) {
+ try {
+ ThreadLocalSession.set(session);
+ HttpSession nativeSession = super.getSession();
+ session.failoverSession(nativeSession);
+ session.putInRegion();
+ } finally {
+ ThreadLocalSession.remove();
+ }
+ }
+ }
+ }
+
+ if (session == null || !session.isValid()) {
+ if (create) {
+ try {
+ session = (GemfireHttpSession) manager.wrapSession(null);
+ ThreadLocalSession.set(session);
+ HttpSession nativeSession = super.getSession();
+ if (session.getNativeSession() == null) {
+ session.setNativeSession(nativeSession);
+ } else {
+ assert (session.getNativeSession() == nativeSession);
+ }
+ session.setIsNew(true);
+ manager.putSession(session);
+ } finally {
+ ThreadLocalSession.remove();
+ }
+ } else {
+ // create is false, and session is either null or not valid.
+ // The spec says return a null:
+ return null;
+ }
+ }
+
+ if (session != null) {
+ addSessionCookie(response);
+ session.updateAccessTime();
+ }
+
+ return session;
+ }
+
+ private void addSessionCookie(HttpServletResponse response) {
+ // Don't bother if the response is already committed
+ if (response.isCommitted()) {
+ return;
+ }
+
+ // Get the existing cookies
+ Cookie[] cookies = getCookies();
+
+ Cookie cookie = new Cookie(manager.getSessionCookieName(),
+ session.getId());
+ cookie.setPath("".equals(getContextPath()) ? "/" : getContextPath());
+ // Clear out all old cookies and just set ours
+ response.addCookie(cookie);
+
+ // Replace all other cookies which aren't JSESSIONIDs
+ if (cookies != null) {
+ for (Cookie c : cookies) {
+ if (manager.getSessionCookieName().equals(c.getName())) {
+ continue;
+ }
+ response.addCookie(c);
+ }
+ }
+
+ }
+
+ private String getCookieString(Cookie c) {
+ StringBuilder cookie = new StringBuilder();
+ cookie.append(c.getName()).append("=").append(c.getValue());
+
+ if (c.getPath() != null) {
+ cookie.append("; ").append("Path=").append(c.getPath());
+ }
+ if (c.getDomain() != null) {
+ cookie.append("; ").append("Domain=").append(c.getDomain());
+ }
+ if (c.getSecure()) {
+ cookie.append("; ").append("Secure");
+ }
+
+ cookie.append("; HttpOnly");
+
+ return cookie.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRequestedSessionIdFromCookie() {
+ return sessionFromCookie;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRequestedSessionIdFromURL() {
+ return sessionFromURL;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getRequestedSessionId() {
+ if (requestedSessionId != null) {
+ return requestedSessionId;
+ } else {
+ return super.getRequestedSessionId();
+ }
+ }
+
+ /*
+ * Hmmm... not sure if this is right or even good to do. So, in some
+ * cases - for ex. using a Spring security filter, we have 3 possible
+ * wrappers to deal with - the original, this one and one created by
+ * Spring. When a servlet or JSP is forwarded to the original request
+ * is passed in, but then this (the wrapped) request is used by the JSP.
+ * In some cases, the outer wrapper also contains information relevant
+ * to the request - in this case security info. So here we allow access
+ * to that. There's probably a better way....
+ */
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Principal getUserPrincipal() {
+ if (outerRequest != null) {
+ return outerRequest.getUserPrincipal();
+ } else {
+ return super.getUserPrincipal();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getRemoteUser() {
+ if (outerRequest != null) {
+ return outerRequest.getRemoteUser();
+ } else {
+ return super.getRemoteUser();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isUserInRole(String role) {
+ if (outerRequest != null) {
+ return outerRequest.isUserInRole(role);
+ } else {
+ return super.isUserInRole(role);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////
+ // Non-API methods
+
+ void setOuterWrapper(HttpServletRequest outer) {
+ this.outerRequest = outer;
+ }
+
+ //////////////////////////////////////////////////////////////
+ // Private methods
+ private String extractSessionId() {
+ final int prefix = getRequestURL().indexOf(URL_SESSION_IDENTIFIER);
+ if (prefix != -1) {
+ final int start = prefix + URL_SESSION_IDENTIFIER.length();
+ int suffix = getRequestURL().indexOf("?", start);
+ if (suffix < 0) {
+ suffix = getRequestURL().indexOf("#", start);
+ }
+ if (suffix <= prefix) {
+ return getRequestURL().substring(start);
+ }
+ return getRequestURL().substring(start, suffix);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * This response wrapper class extends the support class
+ * HttpServletResponseWrapper, which implements all the methods in the
+ * HttpServletResponse interface, as delegations to the wrapped response. You
+ * only need to override the methods that you need to change. You can get
+ * access to the wrapped response using the method getResponse()
+ */
+ class ResponseWrapper extends HttpServletResponseWrapper {
+
+ HttpServletResponse originalResponse;
+
+ public ResponseWrapper(HttpServletResponse response) throws IOException {
+ super(response);
+ originalResponse = response;
+ }
+
+ public HttpServletResponse getOriginalResponse() {
+ return originalResponse;
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ super.setHeader(name, value);
+ }
+
+ @Override
+ public void setIntHeader(String name, int value) {
+ super.setIntHeader(name, value);
+ }
+ }
+
+
+ public SessionCachingFilter() {
+ }
+
+ /**
+ * @param request The servlet request we are processing
+ * @param response The servlet response we are creating
+ * @param chain The filter chain we are processing
+ * @throws IOException if an input/output error occurs
+ * @throws ServletException if a servlet error occurs
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain)
+ throws IOException, ServletException {
+
+ HttpServletRequest httpReq = (HttpServletRequest) request;
+ HttpServletResponse httpResp = (HttpServletResponse) response;
+
+ /**
+ * Early out if this isn't the right kind of request. We might see a
+ * RequestWrapper instance during a forward or include request.
+ */
+ if (request instanceof RequestWrapper ||
+ !(request instanceof HttpServletRequest)) {
+ LOG.debug("Handling already-wrapped request");
+ chain.doFilter(request, response);
+ return;
+ }
+
+ // Create wrappers for the request and response objects.
+ // Using these, you can extend the capabilities of the
+ // request and response, for example, allow setting parameters
+ // on the request before sending the request to the rest of the filter chain,
+ // or keep track of the cookies that are set on the response.
+ //
+ // Caveat: some servers do not handle wrappers very well for forward or
+ // include requests.
+
+ ResponseWrapper wrappedResponse = new ResponseWrapper(httpResp);
+ final RequestWrapper wrappedRequest =
+ new RequestWrapper(manager, httpReq, wrappedResponse);
+
+ Throwable problem = null;
+
+ try {
+ chain.doFilter(wrappedRequest, wrappedResponse);
+ } catch (Throwable t) {
+ // If an exception is thrown somewhere down the filter chain,
+ // we still want to execute our after processing, and then
+ // rethrow the problem after that.
+ problem = t;
+ LOG.error("Exception processing filter chain", t);
+ }
+
+ GemfireHttpSession session =
+ (GemfireHttpSession) wrappedRequest.getSession(false);
+
+ // If there was a problem, we want to rethrow it if it is
+ // a known type, otherwise log it.
+ if (problem != null) {
+ if (problem instanceof ServletException) {
+ throw (ServletException) problem;
+ }
+ if (problem instanceof IOException) {
+ throw (IOException) problem;
+ }
+ sendProcessingError(problem, response);
+ }
+
+ /**
+ * Commit any updates. What actually happens at that point is
+ * dependent on the type of attributes defined for use by the sessions.
+ */
+ if (session != null) {
+ session.commit();
+ }
+ }
+
+ /**
+ * Return the filter configuration object for this filter.
+ */
+ public FilterConfig getFilterConfig() {
+ return (this.filterConfig);
+ }
+
+ /**
+ * Set the filter configuration object for this filter.
+ *
+ * @param filterConfig The filter configuration object
+ */
+ public void setFilterConfig(FilterConfig filterConfig) {
+ this.filterConfig = filterConfig;
+ }
+
+ /**
+ * Destroy method for this filter
+ */
+ @Override
+ public void destroy() {
+ if (manager != null) {
+ manager.stop();
+ }
+ }
+
+ /**
+ * This is where all the initialization happens.
+ *
+ * @param config
+ * @throws ServletException
+ */
+ @Override
+ public void init(final FilterConfig config) {
+ LOG.info("Starting Session Filter initialization");
+ this.filterConfig = config;
+
+ if (started.getAndDecrement() > 0) {
+ /**
+ * Allow override for testing purposes
+ */
+ String managerClassStr =
+ config.getInitParameter("session-manager-class");
+
+ // Otherwise default
+ if (managerClassStr == null) {
+ managerClassStr = GemfireSessionManager.class.getName();
+ }
+
+ try {
+ manager = (SessionManager) Class.forName(
+ managerClassStr).newInstance();
+ manager.start(config, this.getClass().getClassLoader());
+ } catch (Exception ex) {
+ LOG.error("Exception creating Session Manager", ex);
+ }
+
+ startingLatch.countDown();
+ } else {
+ try {
+ startingLatch.await();
+ } catch (InterruptedException iex) {
+ }
+
+ LOG.debug("SessionManager and listener initialization skipped - "
+ + "already done.");
+ }
+
+ LOG.info("Session Filter initialization complete");
+ LOG.debug("Filter class loader {}", this.getClass().getClassLoader());
+ }
+
+ /**
+ * Return a String representation of this object.
+ */
+ @Override
+ public String toString() {
+ if (filterConfig == null) {
+ return ("SessionCachingFilter()");
+ }
+ StringBuilder sb = new StringBuilder("SessionCachingFilter(");
+ sb.append(filterConfig);
+ sb.append(")");
+ return (sb.toString());
+
+ }
+
+
+ private void sendProcessingError(Throwable t, ServletResponse response) {
+ String stackTrace = getStackTrace(t);
+
+ if (stackTrace != null && !stackTrace.equals("")) {
+ try {
+ response.setContentType("text/html");
+ PrintStream ps = new PrintStream(response.getOutputStream());
+ PrintWriter pw = new PrintWriter(ps);
+ pw.print(
+ "<html>\n<head>\n<title>Error</title>\n</head>\n<body>\n"); //NOI18N
+
+ // PENDING! Localize this for next official release
+ pw.print("<h1>The resource did not process correctly</h1>\n<pre>\n");
+ pw.print(stackTrace);
+ pw.print("</pre></body>\n</html>"); //NOI18N
+ pw.close();
+ ps.close();
+ response.getOutputStream().close();
+ } catch (Exception ex) {
+ }
+ } else {
+ try {
+ PrintStream ps = new PrintStream(response.getOutputStream());
+ t.printStackTrace(ps);
+ ps.close();
+ response.getOutputStream().close();
+ } catch (Exception ex) {
+ }
+ }
+ }
+
+ public static String getStackTrace(Throwable t) {
+ String stackTrace = null;
+ try {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ pw.close();
+ sw.close();
+ stackTrace = sw.getBuffer().toString();
+ } catch (Exception ex) {
+ }
+ return stackTrace;
+ }
+
+ /**
+ * Retrieve the SessionManager. This is only here so that tests can get access
+ * to the cache.
+ */
+ public static SessionManager getSessionManager() {
+ return manager;
+ }
+
+ /**
+ * Return the GemFire session which wraps a native session
+ *
+ * @param nativeSession the native session for which the corresponding GemFire
+ * session should be returned.
+ * @return the GemFire session or null if no session maps to the native
+ * session
+ */
+ public static HttpSession getWrappingSession(HttpSession nativeSession) {
+ /*
+ * This is a special case where the GemFire session has been set as a
+ * ThreadLocal during session creation.
+ */
+ GemfireHttpSession gemfireSession = (GemfireHttpSession) ThreadLocalSession.get();
+ if (gemfireSession != null) {
+ gemfireSession.setNativeSession(nativeSession);
+ return gemfireSession;
+ }
+ return getSessionManager().getWrappingSession(nativeSession.getId());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionListener.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionListener.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionListener.java
new file mode 100644
index 0000000..7973bc5
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/filter/SessionListener.java
@@ -0,0 +1,51 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.filter;
+
+import com.gemstone.gemfire.distributed.DistributedSystemDisconnectedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+public class SessionListener implements HttpSessionListener {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(SessionListener.class.getName());
+
+ public void sessionCreated(HttpSessionEvent httpSessionEvent) {
+ }
+
+ /**
+ * This will receive events from the container using the native sessions.
+ */
+ public void sessionDestroyed(HttpSessionEvent event) {
+ String nativeId = event.getSession().getId();
+ try {
+ String sessionId = SessionCachingFilter.getSessionManager().destroyNativeSession(
+ nativeId);
+ LOG.debug(
+ "Received sessionDestroyed event for native session {} (wrapped by {})",
+ nativeId, sessionId);
+ } catch (DistributedSystemDisconnectedException dex) {
+ LOG.debug("Cache disconnected - unable to destroy native session {0}",
+ nativeId);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/Installer.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/Installer.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/Installer.java
new file mode 100644
index 0000000..7ba5b34
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/Installer.java
@@ -0,0 +1,296 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer;
+
+import com.gemstone.gemfire.modules.session.installer.args.Argument;
+import com.gemstone.gemfire.modules.session.installer.args.ArgumentProcessor;
+import com.gemstone.gemfire.modules.session.installer.args.ArgumentValues;
+import com.gemstone.gemfire.modules.session.installer.args.UnknownArgumentHandler;
+import com.gemstone.gemfire.modules.session.installer.args.UsageException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ *
+ */
+public class Installer {
+
+ private static final String GEMFIRE_FILTER_CLASS =
+ "com.gemstone.gemfire.modules.session.filter.SessionCachingFilter";
+
+ private static final String GEMFIRE_LISTENER_CLASS =
+ "com.gemstone.gemfire.modules.session.filter.SessionListener";
+
+ private ArgumentValues argValues;
+
+ private static final Argument ARG_HELP =
+ new Argument("-h", false).
+ setDescription("Displays this help message.");
+
+ private static Argument ARG_GEMFIRE_PARAMETERS =
+ new Argument("-p", false, "param=value").
+ setDescription("Specific parameter for inclusion into the "
+ + "session filter definition as a regular "
+ + "init-param. Can be given multiple times.");
+
+ private static Argument ARG_CACHE_TYPE =
+ new Argument("-t", false, "cache-type").
+ setDescription(
+ "Type of cache. Must be one of 'peer-to-peer' or "
+ + "'client-server'. Default is peer-to-peer.").
+ setDefaults("peer-to-peer");
+
+ private static Argument ARG_WEB_XML_FILE =
+ new Argument("-w", true, "web.xml file").
+ setDescription("The web.xml file to be modified.");
+
+
+ /**
+ * Class main method
+ *
+ * @param args Arguments passed in via the command line
+ * @throws Exception in the event of any errors
+ */
+ public static void main(final String[] args) throws Exception {
+ new Installer(args).process();
+ }
+
+ public static void log(String message) {
+ System.err.println(message);
+ }
+
+
+ public Installer(String[] args) throws Exception {
+ final ArgumentProcessor processor = new ArgumentProcessor("Installer");
+
+ argValues = null;
+ try {
+ // These are ordered so as to keep the options alphabetical
+ processor.addArgument(ARG_HELP);
+ processor.addArgument(ARG_GEMFIRE_PARAMETERS);
+ processor.addArgument(ARG_CACHE_TYPE);
+ processor.addArgument(ARG_WEB_XML_FILE);
+
+ processor.setUnknownArgumentHandler(new UnknownArgumentHandler() {
+ @Override
+ public void handleUnknownArgument(
+ final String form, final String[] params) {
+ log("Unknown argument being ignored: "
+ + form + " (" + params.length + " params)");
+ log("Use '-h' argument to display usage");
+ }
+ });
+ argValues = processor.process(args);
+
+ if (argValues.isDefined(ARG_HELP)) {
+ final UsageException usageException =
+ new UsageException("Usage requested by user");
+ usageException.setUsage(processor.getUsage());
+ throw (usageException);
+ }
+
+ } catch (UsageException ux) {
+ final StringBuilder error = new StringBuilder();
+ error.append("\nERROR: ");
+ error.append(ux.getMessage());
+ error.append("\n");
+ if (ux.getUsage() != null) {
+ error.append(ux.getUsage());
+ }
+ log(error.toString());
+ System.exit(2);
+ }
+
+ }
+
+
+ /**
+ * The main entry point for processing
+ *
+ * @throws Exception if any errors occur.
+ */
+ public void process() throws Exception {
+ String argInputFile = argValues.getFirstResult(ARG_WEB_XML_FILE);
+
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ InputStream input = new FileInputStream(argInputFile);
+
+ processWebXml(input, output);
+ input.close();
+
+ System.out.println(output.toString());
+ }
+
+
+ private void processWebXml(final InputStream webXml,
+ final OutputStream out) throws Exception {
+
+ Document doc = createWebXmlDoc(webXml);
+ mangleWebXml(doc);
+
+ streamXML(doc, out);
+ }
+
+
+ private Document createWebXmlDoc(final InputStream webXml)
+ throws Exception {
+ Document doc;
+ final DocumentBuilderFactory factory =
+ DocumentBuilderFactory.newInstance();
+ final DocumentBuilder builder = factory.newDocumentBuilder();
+ doc = builder.parse(webXml);
+
+ return doc;
+ }
+
+
+ private Document mangleWebXml(final Document doc) {
+ final Element docElement = doc.getDocumentElement();
+ final NodeList nodelist = docElement.getChildNodes();
+ Node firstFilter = null;
+ Node displayElement = null;
+ Node afterDisplayElement = null;
+
+ for (int i = 0; i < nodelist.getLength(); i++) {
+ final Node node = nodelist.item(i);
+ final String name = node.getNodeName();
+ if ("display-name".equals(name)) {
+ displayElement = node;
+ } else {
+ if ("filter".equals(name)) {
+ if (firstFilter == null) {
+ firstFilter = node;
+ }
+ }
+ if (displayElement != null && afterDisplayElement == null) {
+ afterDisplayElement = node;
+ }
+ }
+ }
+
+ Node initParam;
+ final Element filter = doc.createElement("filter");
+ append(doc, filter, "filter-name", "gemfire-session-filter");
+ append(doc, filter, "filter-class", GEMFIRE_FILTER_CLASS);
+
+ // Set the type of cache
+ initParam = append(doc, filter, "init-param", null);
+ append(doc, initParam, "param-name", "cache-type");
+ append(doc, initParam, "param-value",
+ argValues.getFirstResult(ARG_CACHE_TYPE));
+
+
+ if (argValues.isDefined(ARG_GEMFIRE_PARAMETERS)) {
+ for (String[] val : argValues.getAllResults(ARG_GEMFIRE_PARAMETERS)) {
+ String gfParam = val[0];
+ int idx = gfParam.indexOf("=");
+ initParam = append(doc, filter, "init-param", null);
+ append(doc, initParam, "param-name", gfParam.substring(0, idx));
+ append(doc, initParam, "param-value", gfParam.substring(idx + 1));
+ }
+ }
+
+ Node first = firstFilter;
+ if (first == null) {
+ if (afterDisplayElement != null) {
+ first = afterDisplayElement;
+ }
+ }
+ if (first == null) {
+ first = docElement.getFirstChild();
+ }
+ docElement.insertBefore(filter, first);
+ final Element filterMapping = doc.createElement("filter-mapping");
+ append(doc, filterMapping, "filter-name", "gemfire-session-filter");
+ append(doc, filterMapping, "url-pattern", "/*");
+ append(doc, filterMapping, "dispatcher", "FORWARD");
+ append(doc, filterMapping, "dispatcher", "INCLUDE");
+ append(doc, filterMapping, "dispatcher", "REQUEST");
+ append(doc, filterMapping, "dispatcher", "ERROR");
+ final Element contextListener = doc.createElement("listener");
+ append(doc, contextListener, "listener-class", GEMFIRE_LISTENER_CLASS);
+ docElement.insertBefore(filterMapping, after(docElement, "filter"));
+ docElement.insertBefore(contextListener,
+ after(docElement, "filter-mapping"));
+ return doc;
+ }
+
+ private Node after(final Node parent, final String nodeName) {
+ final NodeList nodelist = parent.getChildNodes();
+ int index = -1;
+ for (int i = 0; i < nodelist.getLength(); i++) {
+ final Node node = nodelist.item(i);
+ final String name = node.getNodeName();
+ if (nodeName.equals(name)) {
+ index = i;
+ }
+ }
+ if (index == -1)
+ return null;
+ if (nodelist.getLength() > (index + 1)) {
+ return nodelist.item(index + 1);
+ }
+ return null;
+ }
+
+ private Node append(final Document doc, final Node parent,
+ final String element,
+ final String value) {
+ final Element child = doc.createElement(element);
+ if (value != null)
+ child.setTextContent(value);
+ parent.appendChild(child);
+ return child;
+ }
+
+ private void streamXML(final Document doc, final OutputStream out) {
+ try {// Use a Transformer for output
+ final TransformerFactory tFactory = TransformerFactory.newInstance();
+ final Transformer transformer = tFactory.newTransformer();
+ if (doc.getDoctype() != null) {
+ final String systemId = doc.getDoctype().getSystemId();
+ final String publicId = doc.getDoctype().getPublicId();
+ transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicId);
+ transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemId);
+ }
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount",
+ "4");
+ final DOMSource source = new DOMSource(doc);
+ final StreamResult result = new StreamResult(out);
+ transformer.transform(source, result);
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/JarClassLoader.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/JarClassLoader.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/JarClassLoader.java
new file mode 100644
index 0000000..ba528ce
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/JarClassLoader.java
@@ -0,0 +1,123 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * Classloader, which allows finding classes in jars within jars. This is used to check
+ * whether a listener, as found in web.xml, is a ServletContextListener
+ */
+public class JarClassLoader extends URLClassLoader {
+
+ public JarClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+
+ try {
+ for (URL url : urls) {
+ if (isJar(url.getFile())) {
+ addJarResource(new File(url.getPath()));
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void addJarResource(File file) throws IOException {
+ JarFile jarFile = new JarFile(file);
+ addURL(file.toURL());
+ Enumeration<JarEntry> jarEntries = jarFile.entries();
+ while (jarEntries.hasMoreElements()) {
+ JarEntry jarEntry = jarEntries.nextElement();
+ if (!jarEntry.isDirectory() && isJar(jarEntry.getName())) {
+ addJarResource(jarEntryAsFile(jarFile, jarEntry));
+ }
+ }
+ }
+
+ @Override
+ protected synchronized Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+ try {
+ Class<?> clazz = findLoadedClass(name);
+ if (clazz == null) {
+ clazz = findClass(name);
+ if (resolve) {
+ resolveClass(clazz);
+ }
+ }
+ return clazz;
+ } catch (ClassNotFoundException e) {
+ return super.loadClass(name, resolve);
+ }
+ }
+
+ private static void close(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static boolean isJar(String fileName) {
+ return fileName != null && (fileName.toLowerCase().endsWith(".jar") ||
+ fileName.toLowerCase().endsWith(".war") ||
+ fileName.toLowerCase().endsWith(".ear"));
+ }
+
+ private static File jarEntryAsFile(JarFile jarFile,
+ JarEntry jarEntry) throws IOException {
+ InputStream input = null;
+ OutputStream output = null;
+ try {
+ String name = jarEntry.getName().replace('/', '_');
+ int i = name.lastIndexOf(".");
+ String extension = i > -1 ? name.substring(i) : "";
+ File file = File.createTempFile(
+ name.substring(0, name.length() - extension.length()) + ".",
+ extension);
+ file.deleteOnExit();
+ input = jarFile.getInputStream(jarEntry);
+ output = new FileOutputStream(file);
+ int readCount;
+ byte[] buffer = new byte[4096];
+ while ((readCount = input.read(buffer)) != -1) {
+ output.write(buffer, 0, readCount);
+ }
+ return file;
+ } finally {
+ close(input);
+ close(output);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/Argument.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/Argument.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/Argument.java
new file mode 100644
index 0000000..1125c1b
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/Argument.java
@@ -0,0 +1,275 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer.args;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing a single command line argument.
+ */
+public class Argument {
+
+ /**
+ * Parameter names.
+ */
+ private final String[] paramNames;
+
+ /**
+ * Default values for the parameters when not explicitly set.
+ */
+ private String[] defaults;
+
+ /**
+ * Environment variable names forfor each parameter where values will be
+ * pulled in, if not explicitly provided and if the environment variable
+ * exists.
+ */
+ private String[] envVars;
+
+ /**
+ * Flag indicating whether this argument is required on the command line.
+ */
+ private final boolean required;
+
+ /**
+ * Handler used to hook into processing.
+ */
+ private ArgumentHandler handler;
+
+ /**
+ * List of all representation forms.
+ */
+ private final List<String> forms = new ArrayList<String>();
+
+ /**
+ * Usage description.
+ */
+ private String description;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Constructor:
+
+ /**
+ * Contructor to create an argument definition.
+ *
+ * @param primaryForm the form of the argument (e.g., --foo). Should start
+ * with a dash.
+ * @param argRequired flag indicating whether or not the argument is
+ * required to be onthe command line
+ * @param parameterNames names of the parameters to this argument for use in
+ * the usage generation
+ */
+ public Argument(
+ final String primaryForm,
+ final boolean argRequired,
+ final String... parameterNames) {
+ forms.add(primaryForm);
+ paramNames = parameterNames;
+ required = argRequired;
+ }
+
+ /**
+ * Returns the number of parameters that this argument takes.
+ *
+ * @return parameter count
+ */
+ public int getParameterCount() {
+ return paramNames.length;
+ }
+
+ /**
+ * Returns the name of the parameter position requested.
+ *
+ * @param idx parameter index
+ * @return parameter name
+ */
+ public String getParameterName(final int idx) {
+ return paramNames[idx];
+ }
+
+ /**
+ * Returns whether or not this argument is required to be defined.
+ *
+ * @return true if required, false if optional
+ */
+ public boolean isRequired() {
+ return required;
+ }
+
+ /**
+ * Determines if the argument provisioning has been done via the environment.
+ */
+ public boolean isDefinedInEnv() {
+ if (envVars == null || paramNames.length == 0) {
+ return false;
+ }
+ for (String var : envVars) {
+ if (System.getenv(var) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Sets the argument handler.
+ *
+ * @param aHandler argument handler
+ * @return this argument (for chained calls)
+ */
+ public Argument setArgumentHandler(final ArgumentHandler aHandler) {
+ handler = aHandler;
+ return this;
+ }
+
+ /**
+ * Returns the argument handler.
+ *
+ * @return argument handler
+ */
+ public ArgumentHandler getArgumentHandler() {
+ return handler;
+ }
+
+ /**
+ * Adds a possible representation of the command line argument.
+ *
+ * @param aliasName additional form to accept
+ * @return this argument (for chained calls)
+ */
+ public Argument addForm(final String aliasName) {
+ forms.add(aliasName);
+ return this;
+ }
+
+ /**
+ * Returns the primary form of the argument.
+ *
+ * @return primary form
+ */
+ public String getPrimaryForm() {
+ if (forms.isEmpty()) {
+ return null;
+ } else {
+ return forms.get(0);
+ }
+ }
+
+ /**
+ * Returns a list of all valid representations of this command line argument.
+ *
+ * @return list of all registered forms
+ */
+ public List<String> getForms() {
+ return forms;
+ }
+
+ /**
+ * Sets a usage description for this argument.
+ *
+ * @param str usage description
+ * @return this argument (for chained calls)
+ */
+ public Argument setDescription(final String str) {
+ description = str;
+ return this;
+ }
+
+ /**
+ * Returns a usage description of this argument.
+ *
+ * @return description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the default values when no explicit values were provided.
+ *
+ * @param newDefaults default values for all argument parameters
+ * @return this argument (for chained calls)
+ */
+ public Argument setDefaults(final String... newDefaults) {
+ if (newDefaults.length != paramNames.length) {
+ throw (new IllegalArgumentException(
+ "Defaults array length provided is not the correct size"));
+ }
+ defaults = newDefaults;
+ return this;
+ }
+
+ /**
+ * Returns the defaults.
+ *
+ * @return default parameter values
+ */
+ public String[] getDefaults() {
+ return defaults;
+ }
+
+ /**
+ * Sets the environment variables which will be checked for values before
+ * falling back on the default values.
+ *
+ * @param newEnvVars environment variable name array
+ * @return this argument (for chained calls)
+ */
+ public Argument setEnvVars(final String... newEnvVars) {
+ if (newEnvVars.length != paramNames.length) {
+ throw (new IllegalArgumentException(
+ "Environment variables array length provided is not "
+ + "the correct size"));
+ }
+ envVars = newEnvVars;
+ return this;
+ }
+
+ /**
+ * Returns the environment variable names for each parameter.
+ *
+ * @return environment variable names
+ */
+ public String[] getEnvVars() {
+ return envVars;
+ }
+
+ /**
+ * Returns a human readable form.
+ *
+ * @return human readable string
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("[Argument '");
+ builder.append(forms.get(0));
+ builder.append("'");
+ if (paramNames.length > 0) {
+ for (int i = 0; i < paramNames.length; i++) {
+ builder.append(" <");
+ builder.append(paramNames[i]);
+ builder.append(">");
+ }
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentHandler.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentHandler.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentHandler.java
new file mode 100644
index 0000000..97c8108
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentHandler.java
@@ -0,0 +1,38 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer.args;
+
+/**
+ * Interface specifying the requirements for objects wiching to be able to
+ * examine arguments (potentially tweaking parameters) at the time of parsing,
+ * thereby allowing for usage display to occur automatically.
+ */
+public interface ArgumentHandler {
+
+ /**
+ * Process the argument values specified.
+ *
+ * @param arg argument definition
+ * @param form form which was used on the command line
+ * @param params parameters supplied to the argument
+ * @throws UsageException when usage was suboptimal
+ */
+ void handleArgument(Argument arg, String form, String[] params)
+ throws UsageException;
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentProcessor.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentProcessor.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentProcessor.java
new file mode 100644
index 0000000..04ecfab
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentProcessor.java
@@ -0,0 +1,397 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer.args;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * This class is used to process command line arguments for Java programs in a
+ * flexible and powerful manner.
+ */
+public class ArgumentProcessor {
+ /**
+ * Logger.
+ */
+ private static final Logger LOG =
+ Logger.getLogger(ArgumentProcessor.class.getName());
+
+ /**
+ * Description line length.
+ */
+ private static final int LINE_LENGTH = 60;
+
+ /**
+ * Map containing all arguments defined, indexed by their unique IDs.
+ */
+ private final List<Argument> args = new ArrayList<Argument>();
+
+ /**
+ * Unknown argument handler.
+ */
+ private UnknownArgumentHandler handler;
+
+ /**
+ * Program name to display in usage.
+ */
+ private String programName;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Classes:
+
+ /**
+ * Structure used to represent an argument match.
+ */
+ private static class Match {
+ /**
+ * The argument which matched.
+ */
+ private final Argument arg;
+
+ /**
+ * The specific form which matched.
+ */
+ private final String form;
+
+ /**
+ * The parameters to the argument form.
+ */
+ private final String[] params;
+
+ /**
+ * Constructor.
+ *
+ * @param theArgument the argument which matched
+ * @param theForm the form used
+ * @param theParams the parameters supplied
+ */
+ public Match(
+ final Argument theArgument,
+ final String theForm, final String[] theParams) {
+ arg = theArgument;
+ form = theForm;
+ params = theParams;
+ }
+
+ /**
+ * Accessor.
+ *
+ * @return argument which matched
+ */
+ public Argument getArgument() {
+ return arg;
+ }
+
+ /**
+ * Accessor.
+ *
+ * @return form which was used
+ */
+ public String getForm() {
+ return form;
+ }
+
+ /**
+ * Accessor.
+ *
+ * @return parameters supplied
+ */
+ public String[] getParams() {
+ return params;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Constructors:
+
+ /**
+ * Creates a new Argument processor instance for te program name given.
+ *
+ * @param progName program name used in usage
+ */
+ public ArgumentProcessor(final String progName) {
+ programName = progName;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Public methods:
+
+ /**
+ * Adds a new argument.
+ *
+ * @param arg argument to add
+ */
+ public void addArgument(final Argument arg) {
+ args.add(arg);
+ }
+
+ /**
+ * Sets the handler to call when an unknown argument is encountered.
+ *
+ * @param aHandler unknown arg handler, or null to unset
+ */
+ public void setUnknownArgumentHandler(
+ final UnknownArgumentHandler aHandler) {
+ handler = aHandler;
+ }
+
+ /**
+ * Process the command line arguments provided.
+ *
+ * @param programArgs command line arguments supplied to program
+ * @return argument values parsed out of command line
+ * @throws UsageException when usge sucked
+ */
+ public ArgumentValues process(final String[] programArgs)
+ throws UsageException {
+ ArgumentHandler argHandler;
+ final ArgumentValues result = new ArgumentValues();
+ List<Argument> unmatched;
+ List<Match> matches;
+
+ // Find all argument matches and set postArgs
+ matches = checkMatches(programArgs, result);
+
+ // Find arguments which didnt match
+ unmatched = new ArrayList<Argument>();
+ unmatched.addAll(args);
+ for (Match match : matches) {
+ unmatched.remove(match.getArgument());
+ }
+
+ // Error on unmatched yet required args
+ for (Argument arg : unmatched) {
+ if (arg.isRequired() && !arg.isDefinedInEnv()) {
+ final UsageException usageException = new UsageException(
+ "Required argument not provided: " + arg);
+ usageException.setUsage(getUsage());
+ throw usageException;
+ }
+ }
+
+ // Handle the arguments
+ for (Match match : matches) {
+ final Argument arg = match.getArgument();
+ argHandler = arg.getArgumentHandler();
+ if (argHandler != null) {
+ argHandler.handleArgument(
+ arg, match.getForm(), match.getParams());
+ }
+ result.addResult(arg, match.getParams());
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Generates command line usage text for display to user.
+ *
+ * @return usage to dusplay to user
+ */
+ public String getUsage() {
+ final StringBuilder builder = new StringBuilder();
+ List<String> descriptionLines;
+ final String blank20 = " ";
+
+ builder.append("\nUSAGE: ");
+ if (programName == null) {
+ builder.append("<program>");
+ } else {
+ builder.append(programName);
+ }
+ if (args.isEmpty()) {
+ builder.append("\nNo arguments supported.\n");
+ } else {
+ builder.append(" <args>\nWHERE <args>:\n\n");
+ for (Argument arg : args) {
+ for (String form : arg.getForms()) {
+ builder.append(" ");
+ builder.append(form);
+
+ for (int i = 0; i < arg.getParameterCount(); i++) {
+ builder.append(" <");
+ builder.append(arg.getParameterName(i));
+ builder.append(">");
+ }
+ builder.append("\n");
+ }
+
+ descriptionLines =
+ breakupString(arg.getDescription(), LINE_LENGTH);
+ if (descriptionLines.isEmpty()) {
+ builder.append(blank20);
+ builder.append("No argument description provided.");
+ builder.append("\n\n");
+ } else {
+ for (String line : descriptionLines) {
+ builder.append(blank20);
+ builder.append(line.trim());
+ builder.append("\n");
+ }
+ builder.append("\n");
+ }
+ }
+ }
+ builder.append("\n");
+
+ return builder.toString();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Private methods:
+
+ /**
+ * Builds a listof all argument matches and sets the postArgs array.
+ *
+ * @param programArgs command line arguments to search through
+ * @param values values object in which to store results
+ * @return list of matches
+ * @throws UsageException when there is EBKAC
+ */
+ private List<Match> checkMatches(
+ final String[] programArgs, final ArgumentValues values)
+ throws UsageException {
+ final List<Match> result = new ArrayList<Match>();
+ Match match;
+ String[] params;
+ String[] postArgs;
+ int idx = 0;
+ int idx2;
+
+ while (idx < programArgs.length) {
+ // Check for end-of-parameters arg
+ if ("--".equals(programArgs[idx])) {
+ if (++idx < programArgs.length) {
+ postArgs = new String[programArgs.length - idx];
+ System.arraycopy(programArgs, idx,
+ postArgs, 0, postArgs.length);
+ values.setPostArgs(postArgs);
+ }
+ // We're done processing args'
+ break;
+ }
+
+ // Determine parameter count
+ idx2 = idx;
+ while ((idx2 + 1) < programArgs.length
+ && programArgs[idx2 + 1].charAt(0) != '-') {
+ idx2++;
+ }
+
+ // Generate parameter array
+ params = new String[idx2 - idx];
+ System.arraycopy(programArgs, idx + 1, params, 0, params.length);
+
+ LOG.fine("Arg: " + programArgs[idx]);
+ LOG.fine("Params: " + params.length);
+
+ // Find first argument matches
+ match = null;
+ for (Argument arg : args) {
+ match = checkMatch(programArgs[idx], arg, params);
+ if (match != null) {
+ result.add(match);
+ LOG.fine("Match found: ");
+ LOG.fine(" ID: " + arg);
+ LOG.fine(" Form: " + match.getForm());
+ break;
+ }
+ }
+ if (match == null) {
+ if (handler == null) {
+ final UsageException usageException = new UsageException(
+ "Unknown argument: " + programArgs[idx]
+ + " with " + params.length + " parameters.");
+ usageException.setUsage(getUsage());
+ throw (usageException);
+ } else {
+ handler.handleUnknownArgument(programArgs[idx], params);
+ }
+ }
+
+ idx += params.length + 1;
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks to see if an rgument form matches the suppies parameter list.
+ *
+ * @param argName argument name
+ * @param arg argument
+ * @param params parameters supplied
+ * @return match object on match, null otherwise
+ */
+ private Match checkMatch(
+ final String argName, final Argument arg, final String[] params) {
+ // Look for a matching form
+ for (String form : arg.getForms()) {
+ if (
+ form.equals(argName)
+ && arg.getParameterCount() == params.length) {
+ return new Match(arg, form, params);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Breaks up a string into sub-strings, each with a length equal to or less
+ * than the max length specified.
+ *
+ * @param str string to break up
+ * @param maxLength maximum line length to use
+ * @return broken up string
+ */
+ private List<String> breakupString(
+ final String str, final int maxLength) {
+ final List<String> result = new ArrayList<String>();
+ int startIdx = -1;
+ int lastIdx;
+ int idx;
+
+ if (str == null) {
+ return result;
+ }
+
+ do {
+ idx = startIdx;
+ do {
+ lastIdx = idx;
+ idx = str.indexOf(' ', lastIdx + 1);
+ LOG.fine("startIdx=" + startIdx + " lastIdx=" + lastIdx
+ + " idx=" + idx);
+ if (idx < 0) {
+ // Canot break line up any further
+ result.add(str.substring(startIdx + 1));
+ return result;
+ }
+ } while ((idx - startIdx) <= maxLength);
+
+ result.add(str.substring(startIdx + 1, lastIdx));
+ startIdx = lastIdx;
+ } while (true);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentValues.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentValues.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentValues.java
new file mode 100644
index 0000000..cd412da
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/ArgumentValues.java
@@ -0,0 +1,222 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer.args;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Result object capturing the result of processing command line arguments.
+ */
+public class ArgumentValues {
+
+ /**
+ * Storage location for all arguments found after the "--" pseudo-arg.
+ */
+ private String[] postArgs = new String[]{};
+
+ /**
+ * Storage location for the command line argument values.
+ */
+ private final Map<Argument, List<String[]>> values =
+ new LinkedHashMap<Argument, List<String[]>>();
+
+ /**
+ * Constructor.
+ */
+ ArgumentValues() {
+ // Empty.
+ }
+
+ /**
+ * Sets the post-arguments found after the "--" pseudo-argument.
+ *
+ * @param newPostArgs arguments defined after the special "--" argument
+ */
+ void setPostArgs(final String[] newPostArgs) {
+ postArgs = newPostArgs;
+ }
+
+ /**
+ * After processing the command line arguments, this method may be used to
+ * return all arguments which were excluded from processing by their placement
+ * after the "<code>--</code>" psuedo-argument.
+ *
+ * @return all unprocess arguments
+ */
+ public String[] getPostArgs() {
+ return postArgs;
+ }
+
+ /**
+ * Sets the data values found for a specific argument.
+ *
+ * @param arg argument
+ * @param paramValues parameter values for the argument
+ */
+ public void addResult(final Argument arg, final String[] paramValues) {
+ List<String[]> list = values.get(arg);
+ if (list == null) {
+ list = new ArrayList<String[]>();
+ list.add(paramValues);
+ values.put(arg, list);
+ } else {
+ list.add(paramValues);
+ }
+ }
+
+ /**
+ * Returns a list of all defined arguments.
+ *
+ * @return set of arguments
+ */
+ public Set<Argument> getDefinedArguments() {
+ return values.keySet();
+ }
+
+ /**
+ * Counts the number of arguments defined on the command line which are in the
+ * list provided.
+ *
+ * @param ofThese the arguments to search for, or null to count all supplied
+ * arguments
+ * @return count of the defined arguments
+ */
+ public int getDefinedCount(Argument... ofThese) {
+ if (ofThese.length == 0) {
+ return values.keySet().size();
+ }
+
+ int count = 0;
+ for (Argument arg : values.keySet()) {
+ boolean found = false;
+ for (int i = 0; !found && i < ofThese.length; i++) {
+ if (ofThese[i].equals(arg)) {
+ count++;
+ found = true;
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns whetheror not the command line argument was actually provided on
+ * the command line.
+ *
+ * @param arg argument to query
+ * @return true if the argument is defined by the command line, false
+ * otherwise
+ */
+ public boolean isDefined(final Argument arg) {
+ final List<String[]> result = values.get(arg);
+ return (result != null);
+ }
+
+ /**
+ * Returns all results for the specified argument. If a command line option
+ * is specified more than once, this is the method to use to get all values.
+ *
+ * @param arg argument to query
+ * @return list of all parameter lists defined for this argument
+ */
+ public List<String[]> getAllResults(final Argument arg) {
+ List<String[]> result = values.get(arg);
+
+ if (result == null) {
+ final String[] envVars = arg.getEnvVars();
+ final String[] defaults = arg.getDefaults();
+ final String[] vals = new String[arg.getParameterCount()];
+ boolean found = defaults != null;
+
+ for (int i = 0; i < arg.getParameterCount(); i++) {
+ if (defaults != null) {
+ vals[i] = defaults[i];
+ }
+ if (envVars != null) {
+ String val = System.getenv(envVars[i]);
+ if (val != null) {
+ found = true;
+ vals[i] = val;
+ }
+ }
+ }
+
+ if (found) {
+ result = new ArrayList<String[]>();
+ result.add(vals);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Convenience method to retrieve the first instance of the command line
+ * argument's values.
+ *
+ * @param arg argument to query
+ * @return first parameter list defined for this argument
+ */
+ public String[] getResult(final Argument arg) {
+ final List<String[]> all = getAllResults(arg);
+ if (all == null) {
+ return null;
+ } else {
+ return all.get(0);
+ }
+ }
+
+ /**
+ * Convenience method to return the first value of the first instance of the
+ * command line argument values for the specified argument.
+ *
+ * @param arg argument to query
+ * @return first parameter of the first list of parameters supplied
+ */
+ public String getFirstResult(final Argument arg) {
+ final String[] all = getResult(arg);
+ if (all == null) {
+ return null;
+ } else {
+ return all[0];
+ }
+ }
+
+ /**
+ * Convenience method to return the result of getFirstResult method as an
+ * integer.
+ *
+ * @param arg argument to query
+ * @param undefinedValue value to return when argument is not defined or is
+ * illegally defined
+ * @return value specified, or default value provided
+ */
+ public int getFirstResultAsInt(
+ final Argument arg, final int undefinedValue) {
+ final String value = getFirstResult(arg);
+ if (value == null) {
+ return undefinedValue;
+ } else {
+ return Integer.parseInt(value);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/URLArgumentHandler.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/URLArgumentHandler.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/URLArgumentHandler.java
new file mode 100644
index 0000000..bb4b53a
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/URLArgumentHandler.java
@@ -0,0 +1,77 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer.args;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Argument handler implementation which accepts file paths or URLs and
+ * normalizes the parameters to URLs.
+ */
+public class URLArgumentHandler implements ArgumentHandler {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOG =
+ Logger.getLogger(URLArgumentHandler.class.getName());
+
+ /**
+ * Ensure that the argument is either a file path or a properly formatted URL.
+ * If it is a file path, convert to a URL. If neither, throws a
+ * UsageException.
+ *
+ * @param arg argument
+ * @param form form used
+ * @param parameters parameters supplied
+ * @throws UsageException when file not found or not a workable URL
+ */
+ public void handleArgument(
+ final Argument arg,
+ final String form,
+ final String[] parameters)
+ throws UsageException {
+ final File file = new File(parameters[0]);
+ URL result = null;
+
+ if (file.exists()) {
+ try {
+ result = file.toURI().toURL();
+ } catch (MalformedURLException mux) {
+ LOG.log(Level.FINEST, "Caught Exception", mux);
+ }
+ }
+ if (result == null) {
+ try {
+ result = new URL(parameters[0]);
+ } catch (MalformedURLException mux) {
+ LOG.log(Level.FINEST, "Caught Exception", mux);
+ }
+ }
+ if (result == null) {
+ throw (new UsageException(
+ "Argument parameter value is not a valid file "
+ + "path or URL: " + arg));
+ }
+ parameters[0] = result.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UnknownArgumentHandler.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UnknownArgumentHandler.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UnknownArgumentHandler.java
new file mode 100644
index 0000000..4d52f62
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UnknownArgumentHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package com.gemstone.gemfire.modules.session.installer.args;
+
+/**
+ * Interface defining unknown argument handlers, given the opportunity to either
+ * ignore the issue or force the parameter to be dealt with.
+ */
+public interface UnknownArgumentHandler {
+
+ /**
+ * Called when an unknown argument is supplied.
+ *
+ * @param form argument name used
+ * @param params parameters passed into it
+ * @throws UsageException when the user needs to fix it
+ */
+ void handleUnknownArgument(String form, String[] params)
+ throws UsageException;
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UsageException.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UsageException.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UsageException.java
new file mode 100644
index 0000000..0879417
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/installer/args/UsageException.java
@@ -0,0 +1,89 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.installer.args;
+
+/**
+ * Invalid usage exception.
+ */
+public class UsageException extends Exception {
+
+ /**
+ * Serial format version.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Stored usage message.
+ */
+ private String usage;
+
+ /**
+ * Creates a new UsageException.
+ */
+ public UsageException() {
+ super();
+ }
+
+ /**
+ * Creates a new UsageException.
+ *
+ * @param message description of exceptional condition
+ */
+ public UsageException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new UsageException.
+ *
+ * @param message description of exceptional condition
+ * @param cause provoking exception
+ */
+ public UsageException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Creates a new UsageException.
+ *
+ * @param cause provoking exception
+ */
+ public UsageException(final Throwable cause) {
+ super(cause);
+ }
+
+
+ /**
+ * Attaches a usage message to the exception for later consumption.
+ *
+ * @param usageText text to display to user to guide them to correct usage.
+ * This is generated and set by the <code>ArgsProcessor</code>.
+ */
+ public void setUsage(final String usageText) {
+ usage = usageText;
+ }
+
+ /**
+ * Returns the usage message previously set.
+ *
+ * @return message or null if not set.
+ */
+ public String getUsage() {
+ return usage;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/AbstractSessionCache.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/AbstractSessionCache.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/AbstractSessionCache.java
new file mode 100644
index 0000000..965a97f
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/AbstractSessionCache.java
@@ -0,0 +1,102 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.internal.common;
+
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.modules.session.catalina.internal.DeltaSessionStatistics;
+import com.gemstone.gemfire.modules.session.internal.filter.util.TypeAwareMap;
+import com.gemstone.gemfire.modules.util.RegionConfiguration;
+
+import java.util.Map;
+import javax.servlet.http.HttpSession;
+
+public abstract class AbstractSessionCache implements SessionCache {
+
+ /**
+ * The sessionRegion is the <code>Region</code> that actually stores and
+ * replicates the <code>Session</code>s.
+ */
+ protected Region<String, HttpSession> sessionRegion;
+
+ /**
+ * The operatingRegion is the <code>Region</code> used to do HTTP operations.
+ * if local cache is enabled, then this will be the local <code>Region</code>;
+ * otherwise, it will be the session <code>Region</code>.
+ */
+ protected Region<String, HttpSession> operatingRegion;
+
+ protected Map<CacheProperty, Object> properties =
+ new TypeAwareMap<CacheProperty, Object>(CacheProperty.class);
+
+ protected DeltaSessionStatistics statistics;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() {
+ sessionRegion.close();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Region<String, HttpSession> getOperatingRegion() {
+ return this.operatingRegion;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Region<String, HttpSession> getSessionRegion() {
+ return this.sessionRegion;
+ }
+
+ protected void createStatistics() {
+ this.statistics =
+ new DeltaSessionStatistics(getCache().getDistributedSystem(),
+ (String) properties.get(CacheProperty.STATISTICS_NAME));
+ }
+
+ /**
+ * Build up a {@code RegionConfiguraton} object from parameters originally
+ * passed in as filter initialization parameters.
+ *
+ * @return a {@code RegionConfiguration} object
+ */
+ protected RegionConfiguration createRegionConfiguration() {
+ RegionConfiguration configuration = new RegionConfiguration();
+
+ configuration.setRegionName(
+ (String) properties.get(CacheProperty.REGION_NAME));
+ configuration.setRegionAttributesId(
+ (String) properties.get(CacheProperty.REGION_ATTRIBUTES_ID));
+
+ configuration.setEnableGatewayDeltaReplication(
+ (Boolean) properties.get(
+ CacheProperty.ENABLE_GATEWAY_DELTA_REPLICATION));
+ configuration.setEnableGatewayReplication(
+ (Boolean) properties.get(CacheProperty.ENABLE_GATEWAY_REPLICATION));
+ configuration.setEnableDebugListener(
+ (Boolean) properties.get(CacheProperty.ENABLE_DEBUG_LISTENER));
+
+ return configuration;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/48552465/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/CacheProperty.java
----------------------------------------------------------------------
diff --git a/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/CacheProperty.java b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/CacheProperty.java
new file mode 100644
index 0000000..e26281e
--- /dev/null
+++ b/extensions/gemfire-modules-session/src/main/java/com/gemstone/gemfire/modules/session/internal/common/CacheProperty.java
@@ -0,0 +1,65 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+
+package com.gemstone.gemfire.modules.session.internal.common;
+
+/**
+ * Used to define cache properties
+ */
+public enum CacheProperty {
+
+ ENABLE_DEBUG_LISTENER(Boolean.class),
+
+ ENABLE_GATEWAY_REPLICATION(Boolean.class),
+
+ ENABLE_GATEWAY_DELTA_REPLICATION(Boolean.class),
+
+ ENABLE_LOCAL_CACHE(Boolean.class),
+
+ REGION_NAME(String.class),
+
+ REGION_ATTRIBUTES_ID(String.class),
+
+ STATISTICS_NAME(String.class),
+
+ /**
+ * This parameter can take the following values which match the respective
+ * attribute container classes
+ * <p/>
+ * delta_queued : QueuedDeltaSessionAttributes delta_immediate :
+ * DeltaSessionAttributes immediate : ImmediateSessionAttributes queued
+ * : QueuedSessionAttributes
+ */
+ SESSION_DELTA_POLICY(String.class),
+
+ /**
+ * This parameter can take the following values:
+ * <p/>
+ * set (default) set_and_get
+ */
+ REPLICATION_TRIGGER(String.class);
+
+ Class clazz;
+
+ CacheProperty(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ public Class getClazz() {
+ return clazz;
+ }
+}