You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by tr...@apache.org on 2007/10/23 04:29:48 UTC

svn commit: r587364 [4/7] - in /mina/sandbox/asyncweb: ./ assembly/ assembly/src/ assembly/src/main/ assembly/src/main/descriptor/ core/ core/src/ core/src/main/ core/src/main/java/ core/src/main/java/org/ core/src/main/java/org/safehaus/ core/src/main...

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/AbstractHttpServiceContext.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/AbstractHttpServiceContext.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/AbstractHttpServiceContext.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/AbstractHttpServiceContext.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,171 @@
+/*
+ *  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 org.safehaus.asyncweb.service.context;
+
+import java.net.InetSocketAddress;
+
+import org.safehaus.asyncweb.common.DefaultHttpResponse;
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.safehaus.asyncweb.common.HttpResponse;
+import org.safehaus.asyncweb.common.HttpResponseStatus;
+import org.safehaus.asyncweb.common.MutableHttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.safehaus.asyncweb.service.HttpSession;
+import org.safehaus.asyncweb.service.ServiceContainer;
+import org.safehaus.asyncweb.util.HttpHeaderConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A default implementation of {@link HttpServiceContext}.
+ * 
+ * @author trustin
+ * @version $Rev:167 $, $Date:2006-11-15 11:10:05 +0000 (수, 15 11월 2006) $
+ */
+public abstract class AbstractHttpServiceContext implements HttpServiceContext {
+
+  private final Logger log = LoggerFactory.getLogger(AbstractHttpServiceContext.class);
+  private final InetSocketAddress remoteAddress;
+  private final HttpRequest request;
+  private HttpResponse committedResponse;
+  private HttpSession session;
+  private boolean createdSession;
+  private final ServiceContainer container;
+  
+  public AbstractHttpServiceContext(
+      InetSocketAddress remoteAddress, HttpRequest request, ServiceContainer container) {
+    if (remoteAddress == null) {
+      throw new NullPointerException("remoteAddress");
+    }
+    if (request == null) {
+      throw new NullPointerException("request");
+    }
+    if (container == null) {
+      throw new NullPointerException("container");
+    }
+
+    this.remoteAddress = remoteAddress;
+    this.request = request;
+    this.container = container;
+    this.session = container.getSessionAccessor().getSession(this, false);
+  }
+  
+  public synchronized boolean isResponseCommitted() {
+    return committedResponse != null;
+  }
+
+  /**
+   * Commits a <code>HttpResponse</code> to this <code>Request</code>.
+   * 
+   * @param response  The response to commit
+   * @return <code>true</code> iff the response was committed
+   */
+  public boolean commitResponse(HttpResponse response) {
+    synchronized (this) {
+      if (isResponseCommitted()) {
+        if (log.isDebugEnabled()) {
+          log.info("Request already comitted to a response. Disposing response");
+        }
+        return false;
+      }
+      
+      committedResponse = response;
+    }
+    
+    // Add the session identifier if the session was newly created.
+    if (createdSession) {
+      container.getSessionAccessor().addSessionIdentifier(this, (MutableHttpResponse) response);
+    }
+    
+    // Only parsed requests can be formatted.
+    if (getRequest().getMethod() != null) {
+      container.getErrorResponseFormatter().formatResponse(getRequest(), (MutableHttpResponse) response);
+    }
+    
+    if (container.isSendServerHeader()) {
+      ((MutableHttpResponse) response).setHeader(HttpHeaderConstants.KEY_SERVER, "AsyncWeb");
+    }
+    
+    // Normalize the response.
+    ((MutableHttpResponse) response).normalize(getRequest());
+
+    // Override connection header if needed.
+    if (!container.getKeepAliveStrategy().keepAlive(this, response)) {
+      ((MutableHttpResponse) response).setHeader(
+          HttpHeaderConstants.KEY_CONNECTION, HttpHeaderConstants.VALUE_CLOSE);
+    }
+
+    boolean requiresClosure = !HttpHeaderConstants.VALUE_KEEP_ALIVE.equalsIgnoreCase(
+        response.getHeader(HttpHeaderConstants.KEY_CONNECTION));
+
+    if (requiresClosure && log.isDebugEnabled()) {
+      log.debug("Response status: " + response.getStatus());
+      log.debug("Keep-alive strategy requires closure of " +  getRemoteAddress());
+    }
+    
+    if (log.isDebugEnabled()) {
+      log.debug("Committing a response:");
+      log.debug("Status: " + response.getStatus() + ' ' + response.getStatusReasonPhrase());
+      log.debug("Headers: " + response.getHeaders());
+    }
+    
+    doWrite(requiresClosure);
+    
+    return true;
+  }
+  
+  public boolean commitResponse(HttpResponseStatus status) {
+    MutableHttpResponse response = new DefaultHttpResponse();
+    response.setStatus(status);
+    return commitResponse(response);
+  }
+  
+  public synchronized HttpResponse getCommittedResponse() {
+    return committedResponse;
+  }
+  
+  public InetSocketAddress getRemoteAddress() {
+    return remoteAddress;
+  }
+
+  public HttpRequest getRequest() {
+    return request;
+  }
+
+  public HttpSession getSession() {
+    return getSession(true);
+  }
+  
+  public synchronized HttpSession getSession(boolean create) {
+    if (session != null && !session.isValid()) {
+      session = null;
+    }
+    if (session == null) {
+      session = container.getSessionAccessor().getSession(this, create);
+      if (create) {
+        createdSession = true;
+      }
+    }
+    
+    return session;
+  }
+  
+  protected abstract void doWrite(boolean requiresClosure);
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/AbstractHttpServiceContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/AbstractHttpServiceContext.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/BasicKeepAliveStrategy.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/BasicKeepAliveStrategy.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/BasicKeepAliveStrategy.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/BasicKeepAliveStrategy.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,97 @@
+/*
+ *  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 org.safehaus.asyncweb.service.context;
+
+import org.safehaus.asyncweb.common.HttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Defines common sensible behaviour for the rules under which a
+ * connection should be kept alive.
+ * <code>BasicKeepAliveStrategy</code> will signal that a connection
+ * should be closed iff:
+ * <ul>
+ *   <li>The request is <i>committed</i></li>
+ *   <li>The status of a response forces the connection to be closed</li>
+ *   <li>The request does not want keep-alives</li>
+ * </ul>
+ * Implementations should override <code></code> to provide additional
+ * constraints
+ * 
+ * @author irvingd
+ *
+ */
+public class BasicKeepAliveStrategy implements KeepAliveStrategy {
+
+  private static final Logger LOG = LoggerFactory.getLogger(BasicKeepAliveStrategy.class);
+  
+  /**
+   * Determines whether a connection should be kept alive based
+   * on a response.
+   * This method returns true iff either:
+   * <ul>
+   *   <li>The request is not committed</li>
+   * </u>
+   * or (in order):
+   * <ul>
+   *   <li>The status of a response does not force closure</li>
+   *   <li>The request indicates that it wants the connection to remain
+   *       open</li>
+   *   <li><code>doIsKeepAlive</code> returns true</li>
+   * </ul>
+   * 
+   * @param response  The response to examine
+   * @return <code>true</code> iff a connection should be kept alive
+   */
+  public final boolean keepAlive(HttpServiceContext context, HttpResponse response) {
+    boolean isKeepAlive;
+    if (!context.isResponseCommitted()) {
+      isKeepAlive = true;
+    } else {
+      isKeepAlive = !response.getStatus().forcesConnectionClosure() &&
+                    context.getRequest().isKeepAlive() &&
+                    doIsKeepAlive(context, response);
+      if (!isKeepAlive && LOG.isDebugEnabled() && 
+          response.getStatus().forcesConnectionClosure()) {
+        LOG.debug("Response status forces closure: " + response.getStatus());
+      }
+    }
+    return isKeepAlive;
+  }
+  
+  /**
+   * Simply returns <code>true</code> by default.
+   * Implementations should override this method to provide any
+   * additional keep-alive conditions
+   * 
+   * @param context   The context to check
+   * @param response  The request to check
+   * @return          <code>true</code> if the connection should
+   *                  be kept alive
+   */
+  @SuppressWarnings("unused")
+  protected boolean doIsKeepAlive(HttpServiceContext context, HttpResponse response) {
+    return true;
+  }
+
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/BasicKeepAliveStrategy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/BasicKeepAliveStrategy.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/CounterKeepAliveStrategy.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/CounterKeepAliveStrategy.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/CounterKeepAliveStrategy.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/CounterKeepAliveStrategy.java Mon Oct 22 19:29:38 2007
@@ -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 org.safehaus.asyncweb.service.context;
+
+import org.safehaus.asyncweb.common.HttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+
+
+public class CounterKeepAliveStrategy extends BasicKeepAliveStrategy {
+
+  private static final int DEFAULT_KEEP_ALIVES = 100;
+  
+  private int maxKeepAlives = DEFAULT_KEEP_ALIVES;
+  private int keptAliveCount;
+  
+  /**
+   * @param maxKeepAlives  The maximum number of requests for which
+   *                       a connection should be kept alive
+   */
+  public CounterKeepAliveStrategy(int maxKeepAlives) {
+    this.maxKeepAlives = maxKeepAlives;
+  }
+      
+  /**
+   * Determines whether a connection should be "kept alive" based on
+   * the number of requests for which the connection has previously
+   * been kept alive.
+   * 
+   * @param response  The response to check
+   */
+  protected boolean doIsKeepAlive(HttpServiceContext context, HttpResponse response) {
+    return ++keptAliveCount < maxKeepAlives;
+  }
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/CounterKeepAliveStrategy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/CounterKeepAliveStrategy.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/KeepAliveStrategy.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/KeepAliveStrategy.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/KeepAliveStrategy.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/KeepAliveStrategy.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,46 @@
+/*
+ *  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 org.safehaus.asyncweb.service.context;
+
+import org.safehaus.asyncweb.common.HttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+
+
+/**
+ * Defines a strategy for deciding whether a connection should
+ * remain open after a response has been handled
+ * 
+ * @author irvingd
+ *
+ */
+public interface KeepAliveStrategy {
+
+  /**
+   * Determines whether a connection should remain open after a
+   * response has been handled
+   * 
+   * @param context   The context to check
+   * @param response  The response to check
+   * @return          <code>true</code> iff a connection should
+   *                  remain open after processing the specified response
+   */
+  public boolean keepAlive(HttpServiceContext context, HttpResponse response);
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/KeepAliveStrategy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/context/KeepAliveStrategy.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/CSS.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/CSS.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/CSS.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/CSS.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,72 @@
+/*
+ *  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 org.safehaus.asyncweb.service.errorReporting;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.mina.common.ByteBuffer;
+
+/**
+ * Manages the stylesheet info used for generated pages
+ * 
+ * TODO: Should be moved out to a configuration file when we sort out a
+ *       standard resource strategy
+ *       
+ * @author irvingd
+ *
+ */
+public class CSS {
+  //525D76
+  private static final String BG = "3300cc";
+  
+  private static final String CSS_STRING = 
+    "H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#" + BG + ";font-size:22px;} " +
+    "H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#" + BG + ";font-size:16px;} " +
+    "H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#" + BG + ";font-size:14px;} " +
+    "BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} " +
+    "B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#" + BG + ";} " +
+    "P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}" +
+    "A {color : black;}" +
+    "A.name {color : black;}\n" +
+    "TABLE {cellpadding:20;border-color:black;border-width:1px;border-style:solid;border-collapse:collapse}" + 
+    "TD {border-width:1px;border-color:black;border-style:solid;font-family:Tahoma,Arial,sans-serif;color:black;font-size:12px;}" +
+    "TH {border-width:1px;border-color:black;border-style:solid;font-family:Tahoma,Arial,sans-serif;background-color:FF99FF;color:black;font-size:12px;}" +
+    "HR {color : #" + BG + ";}";
+  
+  private static byte[] BYTES;
+  
+  static {
+    try {
+      BYTES = CSS_STRING.getBytes("US-ASCII");
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException("Must support US-ASCII");
+    }
+  }
+  
+  public static void writeTo(ByteBuffer buf) {
+    buf.put(BYTES);
+  }
+  
+  public static StringBuilder appendTo(StringBuilder buff) {
+    buff.append(CSS_STRING);
+    return buff;
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/CSS.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/CSS.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/ErrorResponseFormatter.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/ErrorResponseFormatter.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/ErrorResponseFormatter.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/ErrorResponseFormatter.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,41 @@
+/*
+ *  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 org.safehaus.asyncweb.service.errorReporting;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.safehaus.asyncweb.common.MutableHttpResponse;
+
+/**
+ * Formats error responses to include a descriptive body where appropriate
+ * 
+ * @author irvingd
+ * 
+ */
+public interface ErrorResponseFormatter {
+
+  /**
+   * Applies any appropriate formatting to a <code>Response</code> based on its
+   * response status code
+   * 
+   * @param response  The response to format
+   */
+  public void formatResponse(HttpRequest request, MutableHttpResponse response);
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/ErrorResponseFormatter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/ErrorResponseFormatter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/StandardResponseFormatter.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/StandardResponseFormatter.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/StandardResponseFormatter.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/StandardResponseFormatter.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,113 @@
+/*
+ *  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 org.safehaus.asyncweb.service.errorReporting;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.safehaus.asyncweb.common.HttpResponseStatus;
+import org.safehaus.asyncweb.common.MutableHttpResponse;
+import org.safehaus.asyncweb.common.HttpResponseStatus.Category;
+import org.safehaus.asyncweb.common.content.ByteBufferContent;
+import org.safehaus.asyncweb.util.StringBundle;
+
+
+public class StandardResponseFormatter implements ErrorResponseFormatter {
+
+  private static final StringBundle bundle 
+    = StringBundle.getBundle(StandardResponseFormatter.class.getPackage().getName());
+  
+  public void formatResponse(HttpRequest request, MutableHttpResponse response) {
+    if (shouldFormat(response)) {
+      doFormat(request, response);
+      response.addHeader("content-type", "text/html");
+    }
+  }
+  
+  private boolean shouldFormat(MutableHttpResponse response) {
+    boolean shouldFormat = false;
+    // FIXME Should be able to handler other content types.
+    if (!(response.getContent() instanceof ByteBufferContent)) {
+      HttpResponseStatus status = response.getStatus();
+      HttpResponseStatus.Category category = response.getStatus().getCategory();
+      shouldFormat = status.allowsMessageBody() && 
+                     (category == Category.CLIENT_ERROR || 
+                      category == Category.SERVER_ERROR);
+    }
+    return shouldFormat;
+  }
+
+  private void doFormat(HttpRequest request, MutableHttpResponse response) {
+    StringBuilder html = new StringBuilder(1024);
+    html.append("<html><head><title>");
+    html.append("AsyncWeb Server - ");
+    html.append(bundle.getString("errorMessage"));
+    html.append("</title><style><!--");
+    CSS.appendTo(html).append("--></style>");
+    html.append("</head></body>");
+    html.append("<h1>");
+    html.append(bundle.getString("errorTitle"));
+    html.append("</h1>");
+    response.getStatusReasonPhrase();
+    String code = String.valueOf(response.getStatus().getCode());
+    html.append("<h1>");
+    html.append(bundle.getString("statusInfo", code));
+    html.append("</h1>");
+    html.append("<HR size=\"1\" noshade=\"noshade\">");
+    
+    html.append("<p><table cellpadding=\"5\">");
+    appendInfo("statusCode", String.valueOf(response.getStatus().getCode()), html);
+    appendInfo("description", getErrorMessage(response), html);
+    appendInfo("requestMethod", request.getMethod().toString(), html);
+    html.append("</table></p>");
+    
+    html.append("<HR size=\"1\" noshade=\"noshade\">");
+    html.append("<H2>AsyncWeb Server</H2>");
+    
+    ByteBuffer out = ByteBuffer.allocate(html.length());
+
+    // TODO: Need to sort this out when we start dealing with character encodings
+    try {
+      byte[] bytes = html.toString().getBytes("US-ASCII");
+      out.put(bytes);
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException(e);
+    }
+    
+    out.flip();
+    response.setContent(new ByteBufferContent(out));
+  }
+  
+  private void appendInfo(String title, String info, StringBuilder html) {
+    html.append("<tr><th>").append(bundle.getString(title)).append("</th>");
+    html.append("<td>").append(info).append("</td>");
+  }
+  
+  private String getErrorMessage(MutableHttpResponse response) {
+    int responseCode = response.getStatus().getCode();
+    String errorMessage = response.getStatusReasonPhrase();
+    if (errorMessage == null) {
+      errorMessage = "";
+    }
+    return bundle.getString("http." + responseCode, errorMessage);
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/StandardResponseFormatter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/errorReporting/StandardResponseFormatter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/SessionKeepAliveFilter.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/SessionKeepAliveFilter.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/SessionKeepAliveFilter.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/SessionKeepAliveFilter.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,75 @@
+/*
+ *  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 org.safehaus.asyncweb.service.filter;
+
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.safehaus.asyncweb.service.HttpServiceFilter;
+import org.safehaus.asyncweb.service.HttpSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A <code>ServiceHandler</code> which causes any existing session associated with
+ * each request it handles to be renewed.
+ * This causes sessions attached to requests to be renewed per-request - even if
+ * the request does not cause session access to occur.<br/>
+ * 
+ * This handler does not need to be installed for deployments which do not employ
+ * sessions
+ * 
+ * @author irvingd
+ *
+ */
+public class SessionKeepAliveFilter implements HttpServiceFilter {
+
+  private static final Logger LOG = LoggerFactory.getLogger(SessionKeepAliveFilter.class);
+  
+  /**
+   * Handles the specified request.
+   * The session associated with the current request - if any - is retrieved - 
+   * causing its lease to be renewed.
+   */
+  public void handleRequest(NextFilter next, HttpServiceContext context) {
+    HttpSession session = context.getSession(false);
+    if (session != null) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Session renewed: " + session.getId());
+      }
+    } else {
+      LOG.debug("No session to renew");
+    }
+    next.invoke();
+  }
+
+  public void start() {
+    // Not interested in startup
+  }
+
+  public void stop() {
+    // Not interested in shutdown    
+  }
+
+  /**
+   * Simply moves the response forward in the chain
+   */
+  public void handleResponse(NextFilter next, HttpServiceContext context) {
+    next.invoke();
+  }
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/SessionKeepAliveFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/SessionKeepAliveFilter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/ThreadPoolFilter.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/ThreadPoolFilter.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/ThreadPoolFilter.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/ThreadPoolFilter.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,138 @@
+/*
+ *  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 org.safehaus.asyncweb.service.filter;
+
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.safehaus.asyncweb.service.HttpServiceFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import EDU.oswego.cs.dl.util.concurrent.BoundedLinkedQueue;
+import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
+
+/**
+ * A <code>ServiceHandler</code> which offloads requests and optionally
+ * responses to a thread pool
+ * 
+ * @author irvingd
+ * @author trustin
+ * @version $Rev$, $Date$
+ */
+public class ThreadPoolFilter implements HttpServiceFilter {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolFilter.class);
+  
+  private static final int DEFAULT_MAX_QUEUE_SIZE = 1000;
+  private static final int DEFAULT_MIN_THREADS    = 1;
+  private static final int DEFAULT_MAX_THREADS    = 10;
+  
+  private PooledExecutor pool;
+  private boolean poolResponses;
+  private int maxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
+  private int minThreads = DEFAULT_MIN_THREADS;
+  private int maxThreads = DEFAULT_MAX_THREADS;
+  
+  /**
+   * Handles the specified request.
+   * A task is scheduled with our thread pool to 
+   * initiate onward filter invocation asyncronously
+   */
+  public void handleRequest(final NextFilter next, HttpServiceContext context) {
+    enqueue(new Runnable() {
+      public void run() {
+        next.invoke();
+      }
+    });
+  }
+
+  /**
+   * Handles the specified response.
+   * If we are configured to offload responses, a task is scheduled
+   * with our thread pool to initiate onward filter invocation
+   * asyncronously
+   */
+  public void handleResponse(final NextFilter next, HttpServiceContext context) {
+    if (poolResponses) {
+      enqueue(new Runnable() {
+        public void run() {
+          next.invoke();
+        }
+      });
+    } else {
+      next.invoke();
+    }
+  }
+
+  /**
+   * Starts this filter - creating and configuring the underlying
+   * thread pool
+   */
+  public void start() {
+    LOG.info("ThreadPoolHandler starting: maxQueueSize=" + maxQueueSize + 
+             " minThreads= " + minThreads + " maxThreads= " + maxThreads + 
+             " poolResponses=" + poolResponses);
+    pool = new PooledExecutor(new BoundedLinkedQueue(maxQueueSize));
+    pool.setMaximumPoolSize(maxThreads);
+    pool.setMinimumPoolSize(minThreads);
+    pool.runWhenBlocked();
+  }
+  
+  public void stop() {
+    // TODO: Clean shut-down
+  }
+  
+  /**
+   * Sets the minimum number of threads to be employed
+   * 
+   * @param minThreads  The minimum number of threads
+   */
+  public void setMinThreads(int minThreads) {
+    this.minThreads = minThreads;
+  }
+  
+  /**
+   * Sets the maximum number of threads to be employed
+   * 
+   * @param maxThreads  The maximum number of threads
+   */
+  public void setMaxThreads(int maxThreads) {
+    this.maxThreads = maxThreads;
+  }
+  
+  /**
+   * Sets the maximum number of requests which are queued for
+   * asyncronous execution, before tasks are run on the calling
+   * thread
+   * 
+   * @param maxQueueSize  The maximum queue size
+   */
+  public void setMaxQueueSize(int maxQueueSize) {
+    this.maxQueueSize = maxQueueSize;  
+  }
+  
+  private void enqueue(Runnable task) {
+    try {
+      pool.execute(task);
+    } catch (InterruptedException e) {
+      LOG.error("Failed to schedule pool task", e);
+    }    
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/ThreadPoolFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/filter/ThreadPoolFilter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipeline.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipeline.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipeline.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipeline.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,64 @@
+/*
+ *  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 org.safehaus.asyncweb.service.pipeline;
+
+import org.safehaus.asyncweb.service.HttpServiceContext;
+
+public interface RequestPipeline {
+
+  /**
+   * Adds a request to this pipeline.
+   * 
+   * @return <code>true</code> iff the pipeline accepts the request
+   */
+  public boolean addRequest(HttpServiceContext context);
+  
+  /**
+   * Frees any responses which may now be provided to the client as a result
+   * of the specified response becoming available.
+   * If the associated request has not been previously added to this pipeline, 
+   * it joins the pipeline at the back of the queue: All previously added 
+   * requests must be responded to before the new request can take its turn<br/>
+   */
+  public void releaseResponse(HttpServiceContext context);
+  
+  
+  /**
+   * Sets the <code>PipelineListener</code> to be notified when
+   * a request is released from this pipeline
+   * 
+   * @param listener  The listener
+   */
+  public void setPipelineListener(RequestPipelineListener listener);
+  
+  /**
+   * Disposes of any requests still living in the pipeline
+   */
+  public void disposeAll();
+  
+  /**
+   * Runs the scheduled command the next time the pipeline is empty.
+   * Run immediately if the pipeline is currently empty;
+   * 
+   * @param r  The command to run
+   */
+  public void runWhenEmpty(Runnable r);
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipeline.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipeline.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipelineListener.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipelineListener.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipelineListener.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipelineListener.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,26 @@
+/*
+ *  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 org.safehaus.asyncweb.service.pipeline;
+
+import org.safehaus.asyncweb.service.HttpServiceContext;
+
+public interface RequestPipelineListener {
+  public void responseReleased(HttpServiceContext context);
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipelineListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/RequestPipelineListener.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/StandardRequestPipeline.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/StandardRequestPipeline.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/StandardRequestPipeline.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/StandardRequestPipeline.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,119 @@
+/*
+ *  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 org.safehaus.asyncweb.service.pipeline;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.safehaus.asyncweb.common.HttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StandardRequestPipeline implements RequestPipeline {
+
+  private static final Logger LOG = LoggerFactory.getLogger(StandardRequestPipeline.class);
+  
+  private int maxPipelinedRequests;
+  private RequestPipelineListener listener;
+  private Runnable emptyCommand;
+  private Map<HttpServiceContext, HttpResponse> entryMap = new LinkedHashMap<HttpServiceContext, HttpResponse>();
+  
+  
+  public StandardRequestPipeline(int maxPipelinedRequests) {
+    this.maxPipelinedRequests = maxPipelinedRequests;
+  }
+  
+  public boolean addRequest(HttpServiceContext context) {
+    boolean added = false;
+    synchronized (entryMap) {
+      if (entryMap.size() < maxPipelinedRequests) {
+        entryMap.put(context, null);
+        added = true;
+      }
+    }
+    if (added && LOG.isDebugEnabled()) {
+      LOG.debug("Request added to pipeline ok");
+    }
+    return added;
+  }
+
+  public void releaseResponse(HttpServiceContext context) {
+    if (context.getCommittedResponse() == null) {
+      throw new IllegalStateException("response is not committed.");
+    }
+    synchronized (entryMap) {
+      entryMap.put(context, context.getCommittedResponse());
+      releaseRequests();
+    }
+  }
+  
+  public void disposeAll() {
+    synchronized (entryMap) {
+      entryMap.clear();
+    }
+  }
+  
+  public void runWhenEmpty(Runnable command) {
+    synchronized (entryMap) {
+      if (entryMap.isEmpty()) {
+        command.run();
+      } else {
+        emptyCommand = command;
+      }
+    }
+  }
+  
+  /**
+   * Sets the pipeline listener associated with this pipeline
+   * 
+   * @param listener The listener
+   */
+  public void setPipelineListener(RequestPipelineListener listener) {
+    this.listener = listener;
+  }
+  
+  /**
+   * Releases any requests which can be freed as a result of a request
+   * being freed.
+   * We simply iterate through the list (in insertion order) - freeing
+   * all responses until we arive at one which has not yet been completed
+   */
+  private void releaseRequests() {
+    for (Iterator<Map.Entry<HttpServiceContext, HttpResponse>> iter = entryMap.entrySet().iterator(); iter.hasNext(); ) {
+      Map.Entry<HttpServiceContext, HttpResponse> entry = iter.next();
+      HttpResponse response = entry.getValue();
+      if (response != null) {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Response freed from pipeline. Notifying");
+        }
+        listener.responseReleased(entry.getKey());
+        iter.remove();
+      } else {
+        break;
+      }
+    }
+    if (emptyCommand != null && entryMap.isEmpty()) {
+      emptyCommand.run();
+    }
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/StandardRequestPipeline.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/pipeline/StandardRequestPipeline.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/CompositeResolver.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/CompositeResolver.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/CompositeResolver.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/CompositeResolver.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,90 @@
+/*
+ *  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 org.safehaus.asyncweb.service.resolver;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A <code>ServiceResolver</code> which applies a list of child
+ * resolvers in turn until a match is made or all children have
+ * been tried
+ * 
+ * @author irvingd
+ *
+ */
+public class CompositeResolver implements ServiceResolver {
+
+  private static final Logger LOG = LoggerFactory.getLogger(CompositeResolver.class);
+  
+  private List<ServiceResolver> resolvers = new ArrayList<ServiceResolver>();
+  
+  /**
+   * Requests all child resolvers to resolve the request until either
+   * a resolution is found, or all child resolvers have been tried.
+   * 
+   * @param  request  The request to resolve
+   * @return The service name, or <code>null</code> if no resolution could
+   *         be found
+   */
+  public String resolveService(HttpRequest request) {
+    for (int i=0, size=resolvers.size(); i < size; ++i) {
+      ServiceResolver resolver = resolvers.get(i);
+      String name = resolver.resolveService(request);
+      if (name != null) {
+        return name;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Adds a resolver. Resolvers are applied in the order they are
+   * added
+   * 
+   * @param resolver  The resolver
+   */
+  public void addResolver(ServiceResolver resolver) {
+    resolvers.add(resolver);
+    LOG.info("Added resolver: " + resolver.getClass());
+  }
+  
+  /**
+   * Sets the resolvers employed by this <code>CompositeResolver</code>
+   * 
+   * @param resolvers  The resolvers
+   */
+  public void setResolvers(List resolvers) {
+    this.resolvers.clear();
+    // Find bad types early
+    for (Iterator iter = resolvers.iterator(); iter.hasNext(); ) {
+      ServiceResolver resolver = (ServiceResolver) iter.next();
+      
+      addResolver(resolver);
+    }
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/CompositeResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/CompositeResolver.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ExactMatchURIServiceResolver.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ExactMatchURIServiceResolver.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ExactMatchURIServiceResolver.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ExactMatchURIServiceResolver.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,105 @@
+/*
+ *  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 org.safehaus.asyncweb.service.resolver;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A <code>ServiceResolver</code> which maps request URIs to
+ * service names. An exact match is required on a request URI to
+ * a registered URI must be made for a service name to be resolved
+ * 
+ * @author irvingd
+ * FIXME Rename to ExactMatchPathServiceResolver
+ */
+public class ExactMatchURIServiceResolver implements ServiceResolver {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ExactMatchURIServiceResolver.class);
+  
+  private Map<String, String> serviceMap = new HashMap<String, String>();
+  
+  /**
+   * Adds a mapping from a request URI to a service name.
+   * Any existing mapping for the same URI is overwritten.
+   * 
+   * @param uri          The URI
+   * @param serviceName  The service name
+   */
+  public void addURIMapping(String uri, String serviceName) {
+    String existingMapping = serviceMap.put(uri, serviceName);
+    if (existingMapping != null) {
+      LOG.info("Existing service [" + existingMapping + "] replaced by " + 
+               "[" + serviceName + "] for URI [" + uri + "]");
+    } else {
+      LOG.info("Mapped [" + uri + "] to service [" + serviceName + "]");
+    }
+  }
+  
+  /**
+   * Sets all uri - service name mappings from a given map.
+   * Any existing mappings are removed
+   * 
+   * @param map                  The map to set from
+   * @throws ClassCastException  If any element (key or value) in the map
+   *                             is not a <code>java.lang.String</code>
+   */
+  public void setMappings(Map map) {
+    serviceMap.clear();
+    for (Iterator iter=map.entrySet().iterator(); iter.hasNext(); ) {
+      Map.Entry entry = (Map.Entry) iter.next();
+      String key   = (String) entry.getKey();
+      String value = (String) entry.getValue();
+      addURIMapping(key, value);
+    }
+  }
+  
+  /**
+   * Attempts to resolve a service name for the specified request by
+   * looking for an existing matching with the same URI as the specified 
+   * request.
+   * 
+   * @param request  The request for which a service name is to be resolved
+   * @return         The name of the service, or <code>null</code> if no
+   *                 mapping exists for the requests URI
+   */
+  public String resolveService(HttpRequest request) {
+    if (request.getRequestUri().isAbsolute()) {
+      return null;
+    }
+
+    String path = request.getRequestUri().getPath();
+    String serviceName = serviceMap.get(path);
+    if (LOG.isDebugEnabled()) {
+      if (serviceName == null) {
+        LOG.debug("No mapping for path [" + path + "]");
+      } else {
+        LOG.debug("Mapped [" + path + "] to service [" + serviceName + "]");
+      }
+    }
+    return serviceName;
+  }
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ExactMatchURIServiceResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ExactMatchURIServiceResolver.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/FixedServiceResolver.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/FixedServiceResolver.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/FixedServiceResolver.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/FixedServiceResolver.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,41 @@
+/*
+ *  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 org.safehaus.asyncweb.service.resolver;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+
+/**
+ * @author Dan Diephouse
+ */
+public class FixedServiceResolver implements ServiceResolver {
+    private String service;
+    
+    public String resolveService(HttpRequest request) {
+        return service;
+    }
+
+    public String getService() {
+        return service;
+    }
+
+    public void setService(String service) {
+        this.service = service;
+    }
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/FixedServiceResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/FixedServiceResolver.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/PassThruResolver.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/PassThruResolver.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/PassThruResolver.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/PassThruResolver.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,53 @@
+/*
+ *  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 org.safehaus.asyncweb.service.resolver;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+
+/**
+ * A simple <code>ServiceResolver</code> which passes the full
+ * request URI as the service name, optionally removing any leading
+ * "/"
+ * 
+ * @author irvingd
+ *
+ */
+public class PassThruResolver implements ServiceResolver {
+
+  private boolean removeLeadingSlash = true;
+  
+  public String resolveService(HttpRequest request) {
+    if (request.getRequestUri().isAbsolute()) {
+      return null;
+    }
+
+    String path = request.getRequestUri().getPath();
+    int length = path.length();
+    if (removeLeadingSlash && length > 0 && path.charAt(0) == '/') {
+      path = length > 1 ? path.substring(1) : "";
+    }
+    return path;
+  }
+
+  public void setRemoveLeadingSlash(boolean removeLeadingSlash) {
+    this.removeLeadingSlash = removeLeadingSlash;
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/PassThruResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/PassThruResolver.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ServiceResolver.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ServiceResolver.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ServiceResolver.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ServiceResolver.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,48 @@
+/*
+ *  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 org.safehaus.asyncweb.service.resolver;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+
+/**
+ * Resolves the name of the <code>HttpService</code> to be used to
+ * service a request.
+ * 
+ * @author irvingd
+ *
+ */
+public interface ServiceResolver {
+
+  /**
+   * Determines the name of the <code>HttpService</code> to be employed
+   * to service a given <code>HttpRequest</code>.<br/>
+   * 
+   * If no service can be resolved for the specified request, this method
+   * should return <code>null</code>.
+   * 
+   * @param request  The request for which a service name is required
+   * @return         The name of the <code>HttpService</code> which
+   *                 should be used to service the request, or
+   *                 <code>null</code> if no appropriate service can be
+   *                 found
+   */
+  public String resolveService(HttpRequest request);
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ServiceResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/ServiceResolver.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/SimplePrefixResolver.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/SimplePrefixResolver.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/SimplePrefixResolver.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/SimplePrefixResolver.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,106 @@
+/*
+ *  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 org.safehaus.asyncweb.service.resolver;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A very simple resolver which simply uses the full URI after
+ * stripping an optional prefix as the name of the service.
+ * 
+ * <code>SimplePrefixResolver</code> is useful when a very
+ * naming scheme is used - and allows services to be resolved
+ * "dynamically" without any global configuration.<br/>
+ * </br>
+ * Request URIs which do not begin with the configured prefix are
+ * not resolved.<br/>
+ * <br/>
+ * For example, suppose all <code>HttpService</code>s are addressed
+ * under the prefix <code>/services/</code>. We would then map as
+ * follows for the following URIs:<br/>
+ * <br/>
+ * <table border="1" cellpadding="2">
+ *  <tr><td>URI</td><td>Service Name</td></tr>
+ *  <tr><td>/services/serviceA</td><td>serviceA</td></tr>
+ *  <tr><td>/services/serviceB</td><td>serviceB</td></tr>
+ *  <tr><td>/services/x/serviceX</td><td>x/serviceA</td></tr>
+ *  <tr><td>/x/serviceA</td><td>null</td></tr>
+ * </table>
+ * 
+ * @author irvingd
+ *
+ */
+public class SimplePrefixResolver implements ServiceResolver {
+
+  private static final Logger LOG = LoggerFactory.getLogger(SimplePrefixResolver.class);
+  
+  // FIXME Rename to pathPrefix.
+  private String uriPrefix;
+
+  /**
+   * Sets the prefix associated with this resolver.
+   * URIs which begin with the specified prefix are resolved to the URI
+   * with the prefix stripped. URIs which do not begin with the specified
+   * prefix are not resolved.<br/>
+   * <br/>
+   * If a prefix is not set, requests are resolved to their URI value.
+   * 
+   * @param uriPrefix  The uri prefix to apply
+   */
+  public void setUriPrefix(String uriPrefix) {
+    this.uriPrefix = uriPrefix;  
+    LOG.info("URI prefix: " + uriPrefix);
+  }
+  
+  /**
+   * Resolves the name of the service to be employed for the specified request.
+   * If this resolver is not configured with a prefix, the request resoves to
+   * the request URI.<br/>
+   * Otherwise, if the request URI begins with the configured prefix, the request
+   * resolves to the URI with the prefix stripped. If the request URI does not
+   * begin with the configured prefix, the request is unresolved
+   * 
+   * @param request The request to resolve to a service name
+   * @return        The resolved service name, or <code>null</code> if
+   *                the request is un-resolved
+   */
+  public String resolveService(HttpRequest request) {
+    if (request.getRequestUri() == null || request.getRequestUri().isAbsolute()) {
+      return null;
+    }
+
+    String path = request.getRequestUri().getPath();
+    if (uriPrefix != null && path != null) {
+      if (path.startsWith(uriPrefix)) {
+        path = path.substring(uriPrefix.length());
+      } else {
+        path = null;
+      }
+    }
+    if (LOG.isInfoEnabled()) {
+      LOG.info("Resolved request to service name: " + path);
+    }
+    return path;
+  }
+
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/SimplePrefixResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/resolver/SimplePrefixResolver.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSession.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSession.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSession.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSession.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,140 @@
+/*
+ *  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 org.safehaus.asyncweb.service.session;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.safehaus.asyncweb.service.HttpSession;
+import org.safehaus.asyncweb.util.TimedPermit;
+
+/**
+ * A basic <code>Session</code> implementation which holds session values
+ * in memory
+ * 
+ * @author irvingd
+ *
+ */
+class BasicSession implements HttpSession {
+
+  private TimedPermit permit;
+  private boolean isAttached;
+  private boolean isDestroyed;
+  private Object lock = new Object();
+  private BasicSessionStore owner;
+  private String id;
+  
+  private Map<String, Object> values = Collections.synchronizedMap(new HashMap<String, Object>());
+  
+  /**
+   * @param owner  The owner of this session
+   */
+  BasicSession(String id, BasicSessionStore owner) {
+    this.owner = owner;
+    this.id    = id;
+  }
+  
+  public Object getValue(String key) {
+    return values.get(key);
+  }
+
+  public void setValue(String key, Object value) {
+    values.put(key, value);
+  }
+
+  public Object removeValue(String key) {
+    return values.remove(key);
+  }
+
+  public boolean isAttached() {
+    return isAttached;
+  }
+
+  public boolean isValid() {
+    synchronized (lock) {
+      return !isDestroyed;
+    }
+  }
+  
+  /**
+   * Destroys this session if it is not already destroyed.
+   * If destruction takes place, notifications are fired.
+   */
+  public void destroy() {
+    if (destroyIfActive()) {
+      owner.sessionDestroyed(this);
+    }
+  }
+
+  /**
+   * @return  The id of this session
+   */
+  public String getId() {
+    return id;
+  }
+  
+  /**
+   * Instructs this session to expire.
+   * If this session is not already expired, expiration notifications
+   * are fired
+   */
+  void expire() {
+    if (destroyIfActive()) {
+      owner.sessionExpired(this);
+    }
+  }
+  
+  /**
+   * Sets the permit associated with this session
+   * 
+   * @param permit  This sessions access permit
+   */
+  void setPermit(TimedPermit permit) {
+    this.permit = permit;
+  }
+  
+  /**
+   * Invoked when this session is referenced by a client request.
+   * The permit associated with this session is renewed, and subsequent calls
+   * to <code>isAttached</code> will return <code>true</code>
+   */
+  void access() {
+    permit.renew();
+    isAttached = true;
+  }
+  
+  /**
+   * Destroys this session if it is not destroyed already.
+   * 
+   * @return  <code>true</code> if this session is successfully
+   */
+  private boolean destroyIfActive() {
+    synchronized (lock) {
+      if (isDestroyed) {
+        return false;
+      }
+      isDestroyed = true;
+    }
+    permit.cancel();
+    return true;
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSession.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSession.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSessionStore.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSessionStore.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSessionStore.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSessionStore.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,277 @@
+/*
+ *  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 org.safehaus.asyncweb.service.session;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.safehaus.asyncweb.service.HttpSession;
+import org.safehaus.asyncweb.util.LinkedPermitIssuer;
+import org.safehaus.asyncweb.util.PermitExpirationListener;
+import org.safehaus.asyncweb.util.TimedPermit;
+import org.safehaus.asyncweb.util.TimedPermitIssuer;
+
+/**
+ * A simple <code>SessionStore</code> implementation which holds all session
+ * data in memory. A <code>TimedPermitIssuer</code> is employed to issue a time-out
+ * permit for each issued session.
+ * 
+ * @author irvingd
+ *
+ */
+public class BasicSessionStore implements HttpSessionStore {
+
+  private static final Logger LOG = LoggerFactory.getLogger(BasicSessionStore.class);
+  
+  /**
+   * Default session timeout of 15 minutes
+   */
+  private static final long DEFAULT_SESSION_TIMEOUT = 900000;
+  
+  private Map<String, HttpSession> sessionMap = Collections.synchronizedMap(new HashMap<String, HttpSession>());
+  private List<HttpSessionListener> listeners = Collections.synchronizedList(new ArrayList<HttpSessionListener>());
+  
+  private TimedPermitIssuer permitIssuer;
+  private boolean isClosed;
+  
+  /**
+   * Constructs with the default session timeout
+   */
+  public BasicSessionStore() {
+    this(DEFAULT_SESSION_TIMEOUT);
+  }
+  
+  /**
+   * Constructs with a specified session timeout
+   * 
+   * @param sessionTimeout  The session timeout (in ms)
+   */
+  public BasicSessionStore(long sessionTimeout) {
+    permitIssuer = new LinkedPermitIssuer(sessionTimeout);  
+    permitIssuer.addPermitExpirationListener(new TimeoutListener());
+    LOG.info("BasicSessionStore timeout: " + sessionTimeout + "ms");
+  }
+  
+  /**
+   * Adds a listener to this store
+   * 
+   * @param listener The listener to add
+   */
+  public void addSessionListener(HttpSessionListener listener) {
+    listeners.add(listener);
+  }
+  
+  /**
+   * Sets the listeners employed by this store.
+   * Any existing listeners are removed
+   * 
+   * @param listeners  The listeners to be added
+   */
+  public void setSessionListeners(Collection<HttpSessionListener> listeners) {
+    synchronized (this.listeners) {
+      this.listeners.clear();
+      this.listeners.addAll(listeners);
+    }
+  }
+  
+  /**
+   * Closes this store.
+   * Our permit issuer is closed, and all sessions are destroyed.
+   */
+  public void close() {
+    List<HttpSession> closureList = null;
+    
+    synchronized (sessionMap) {
+      if (isClosed) {
+        LOG.debug("Already closed");
+        return;
+      }
+      LOG.debug("BasicSessionStore closing");
+      permitIssuer.close();
+      isClosed = true;
+      closureList = new ArrayList<HttpSession>(sessionMap.values());
+    }
+    for (Iterator<HttpSession> iter = closureList.iterator(); iter.hasNext(); ) {
+      BasicSession session = (BasicSession) iter.next();
+      LOG.debug("Closure: Destroying session: " + session.getId());
+      session.destroy();
+    }
+  }
+
+  /**
+   * Creates a new session for the specified key.
+   * 
+   * @param key  The session key
+   * @return The created session, or <code>null</code> if a session is already
+   *         held for the specified key
+   */
+  public HttpSession createSession(String key) {
+    BasicSession created = null;
+    synchronized (sessionMap) {
+      if (isClosed) {
+        throw new IllegalStateException("Store closed");
+      }
+      if (!sessionMap.containsKey(key)) {
+        created = new BasicSession(key, this);
+        sessionMap.put(key, created);
+        TimedPermit permit = permitIssuer.issuePermit(created);
+        created.setPermit(permit);
+      }
+    }
+    if (created != null) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("New session created with key '" + key + "'. Firing notifications");
+      }
+      fireCreated(created);
+    }
+    return created;
+  }
+
+  /**
+   * Locates the session with the specified key.
+   * If the session is found, we request it to renew its access permit.
+   * 
+   * @param key  The key for which a session is required
+   * @return     The located session, or <code>null</code> if no session with the
+   *             specified key was found
+   */
+  public HttpSession locateSession(String key) {
+    BasicSession session = (BasicSession) sessionMap.get(key);
+    if (session != null) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Located session with key '" + key + "'. Marking as accessed");
+      }
+      session.access();
+    }
+    return session;
+  }
+
+  /**
+   * Invoked by a session we created when it successfully processes an expiry.
+   * The session is removed from our session map, and expiry notifications are fired.
+   * 
+   * @param session  The expired session
+   */
+  void sessionExpired(BasicSession session) {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Session has been expired. Processing notifications for '" + session.getId() + "'");
+    }
+    sessionMap.remove(session.getId());
+    fireExpiry(session);  
+  }
+  
+  /**
+   * Invoked by a session we created when it successfully processes a destruction
+   * request.
+   * The session is removed from our session map, and destruction notifications
+   * are fired
+   * 
+   * @param session  The destroyed session
+   */
+  void sessionDestroyed(BasicSession session) {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Session has been destroyed. Processing notifications for '" + session.getId() + "'");
+    }
+    sessionMap.remove(session.getId());
+    fireDestroyed(session);
+  }
+  
+  /**
+   * Invoked when the permit associated with the specified session expires.
+   * We simply request the session to expire itself.
+   * If the session is not already destroyed, it will request us to fire
+   * notifications on its behalf.
+   * 
+   * @param session  The expired session
+   */
+  private void sessionPermitExpired(BasicSession session) {
+    session.expire();
+  }
+  
+  /**
+   * Fires creation notification to all listeners assocaited with this store
+   * 
+   * @param session  The expired session
+   */
+  private void fireCreated(HttpSession session) {
+    synchronized (listeners) {
+      for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
+        HttpSessionListener listener = (HttpSessionListener) iter.next();
+        listener.sessionCreated(session);
+      }
+    }    
+  }
+  
+  /**
+   * Fires destruction notification to all listeners assocaited with this store
+   * 
+   * @param session  The expired session
+   */
+  private void fireDestroyed(HttpSession session) {
+    synchronized (listeners) {
+      for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
+        HttpSessionListener listener = (HttpSessionListener) iter.next();
+        listener.sessionDestroyed(session);
+      }
+    }    
+  }
+  
+  /**
+   * Fires expiry notification to all listeners assocaited with this store
+   * 
+   * @param session  The expired session
+   */
+  private void fireExpiry(HttpSession session) {
+    synchronized (listeners) {
+      for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
+        HttpSessionListener listener = (HttpSessionListener) iter.next();
+        listener.sessionExpired(session);
+      }
+    }
+  }
+  
+  /**
+   * Receives notifications of timed out permits issued by this store,
+   * and triggers expiry of the associated session
+   * 
+   * @author irvingd
+   *
+   */
+  private class TimeoutListener implements PermitExpirationListener {
+
+    /**
+     * Invoked when a permit issued for a session expires
+     * 
+     * @param session  The session which has expired
+     */
+    public void permitExpired(Object session) {
+      sessionPermitExpired((BasicSession)session);
+    }
+    
+  }
+  
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSessionStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/BasicSessionStore.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/CookieIdentifier.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/CookieIdentifier.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/CookieIdentifier.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/CookieIdentifier.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,87 @@
+/*
+ *  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 org.safehaus.asyncweb.service.session;
+
+import org.safehaus.asyncweb.common.Cookie;
+import org.safehaus.asyncweb.common.DefaultCookie;
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.safehaus.asyncweb.common.MutableCookie;
+import org.safehaus.asyncweb.common.MutableHttpResponse;
+
+
+/**
+ * A <code>SessionIdentifier</code> which adds and extracts session key
+ * cookies
+ * 
+ * @author irvingd
+ *
+ */
+public class CookieIdentifier implements HttpSessionIdentifier {
+
+  /**
+   * The name of the cookie
+   */
+  private static final String SESSION_ID_COOKIE = "sessionKey";
+  
+  private String cookieId = SESSION_ID_COOKIE;
+  
+  /**
+   * Sets the name of the cookie used for holding session keys.
+   * The default cookie name is <code>sessionKey</code>
+   * 
+   * @param cookieId  The cookie name to be used
+   */
+  public void setCookieId(String cookieId) {
+    this.cookieId = cookieId;
+  }
+  
+  /**
+   * Extracts a session key from the session cookie supplied with the request - 
+   * if any
+   * 
+   * @param  request  The request
+   * @return The session key, or null if a session cookie was not located
+   */
+  public String getSessionKey(HttpRequest request) {
+    Cookie sessionCookie = null;
+    for (Cookie c: request.getCookies()) {
+      if (c.getName().equals(cookieId)) {
+        sessionCookie = c;
+        break;
+      }
+    }
+    return sessionCookie == null ? null : sessionCookie.getValue();
+  }
+
+  /**
+   * Adds a session cookie to the specified request
+   * 
+   * @param key      The session key
+   * @param response The response
+   */
+  public void addSessionKey(String key, MutableHttpResponse response) {
+    MutableCookie sessionCookie = new DefaultCookie(cookieId);
+    sessionCookie.setMaxAge(-1); // "non-persistent"
+    sessionCookie.setValue(key);
+    // TODO: Set "isSecure" based on whether the request came in over
+    //       a secure transport
+    response.addCookie(sessionCookie);
+  }
+}

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/CookieIdentifier.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/CookieIdentifier.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date