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 [5/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/session/DefaultSessionAccessor.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/DefaultSessionAccessor.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/DefaultSessionAccessor.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/DefaultSessionAccessor.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,155 @@
+/*
+ * 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.MutableHttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.safehaus.asyncweb.service.HttpSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A simple <code>SessionAccessor</code> implementation which acts as a facade
+ * to an employed <code>SessionIdentifier</code>, <code>SessionKeyFactory</code>
+ * and <code>SessionStore</code>.<br/>
+ *
+ * A Default identifier and key factory is employed by this accessor, but the
+ * implementations used can be switched (if required) using the appropriate
+ * setter methods.<br/>
+ *
+ * @author irvingd
+ *
+ */
+public class DefaultSessionAccessor implements HttpSessionAccessor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultSessionAccessor.class);
+
+ private HttpSessionIdentifier identifier = new CookieIdentifier();
+ private HttpSessionKeyFactory keyFactory;
+ private HttpSessionStore store;
+
+ /**
+ * Constructs with the default identifier and key factory
+ */
+ public DefaultSessionAccessor() {
+ SecureRandomKeyFactory secureKeyFactory = new SecureRandomKeyFactory();
+ secureKeyFactory.start();
+ keyFactory = secureKeyFactory;
+ }
+
+ public HttpSession getSession(HttpServiceContext context, boolean create) {
+ String sessionKey = identifier.getSessionKey(context.getRequest());
+ HttpSession session = null;
+ if (sessionKey != null) {
+ LOG.debug("Request contains session key - attempting to lookup");
+ session = store.locateSession(sessionKey);
+ if (session == null) {
+ LOG.debug("No session found with request's session key");
+ }
+ }
+ if (session == null && create) {
+ LOG.debug("No existing session found for request - creating new session");
+ session = createNewSession();
+ }
+ return session;
+ }
+
+ public void addSessionIdentifier(HttpServiceContext context, MutableHttpResponse response) {
+ HttpSession session = context.getSession(false);
+ if (session == null) {
+ return;
+ }
+ identifier.addSessionKey(session.getId(), response);
+ }
+
+ /**
+ * Sets the <code>SessionIdentifier</code> used for encoding / decoding
+ * session keys to / from requests.
+ * By default, a <code>CookieIdentifier</code> is employed
+ *
+ * @param identifier The identifier to be employed
+ */
+ public void setSessionIdentifier(HttpSessionIdentifier identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Sets the <code>SessionKeyFactory</code> employed by this accessor for
+ * creating new session keys.
+ * By default, a <code>SecureRandomKeyFactory</code> is employed
+ *
+ * @param keyFactory The key factory to be employed
+ */
+ public void setSessionKeyFactory(HttpSessionKeyFactory keyFactory) {
+ this.keyFactory = keyFactory;
+ }
+
+ /**
+ * Sets the session store employed by this accessor
+ *
+ * @param store The store
+ */
+ public void setSessionStore(HttpSessionStore store) {
+ if (this.store != null) {
+ this.store.close();
+ }
+ this.store = store;
+ }
+
+ /**
+ * Disposes of this accessor. We simply close our store
+ */
+ public void dispose() {
+ store.close();
+ }
+
+ /**
+ * Initialises this accessor. If we have not been configured with a session store,
+ * we simply employ a <code>BasicSessionStore</code> with a default time out
+ */
+ public void init() {
+ if (store == null) {
+ LOG.info("No session store configured. Employing default session store");
+ store = new BasicSessionStore();
+ }
+ }
+
+ /**
+ * Establishes a new session with the specified response.
+ * We employ our <code>SessionKeyFactory</code> to generate a new session
+ * key, and request our <code>SessionStore</code> to create a session based
+ * on this id.<br/>
+ * Our key factory is designed to not provide duplicate keys, but if this occurs
+ * we cycle until an unused key is located.
+ */
+ private HttpSession createNewSession() {
+ HttpSession session;
+ String sessionKey;
+ do {
+ sessionKey = keyFactory.createSessionKey();
+ session = store.createSession(sessionKey);
+ if (session == null) {
+ LOG.warn("SessionKeyFactory is providing duplicate keys!!");
+ }
+ } while (session == null);
+ LOG.debug("New session created");
+ return session;
+ }
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/DefaultSessionAccessor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/DefaultSessionAccessor.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionAccessor.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionAccessor.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionAccessor.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionAccessor.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,69 @@
+/*
+ * 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.MutableHttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.safehaus.asyncweb.service.HttpSession;
+
+
+/**
+ * A facade through which <code>Session</code>s are created and accessed.
+ * A <code>SessionAccessor</code> provides a simple interface for accessing
+ * the Session associated with a request (and optionally creating it if required)
+ *
+ * @author irvingd
+ *
+ */
+public interface HttpSessionAccessor {
+
+ /**
+ * Attempts to locate the session associated with the specified context.
+ * If no session can be located based on the request, and <code>create</code>
+ * is <code>true</code>, a new session is created and bound against the request.
+ * Otherwise, if a session is not currently bound against the specified request
+ * and <code>create</code> is false, this method returns <code>null</code>
+ *
+ * @param context The context for which a session is required
+ * @param create If this parameter is <code>true</code> and no session can
+ * be found for the given request, a new session is created
+ * and bound against the request
+ * @return The located / created session - <code>null</code> if an
+ * existing session can not be located and <code>create</code>
+ * is <code>false</code>
+ */
+ public HttpSession getSession(HttpServiceContext context, boolean create);
+
+ /**
+ * Adds session identifier to the specified response.
+ */
+ public void addSessionIdentifier(HttpServiceContext context, MutableHttpResponse response);
+
+ /**
+ * Prepares this accessor for use
+ */
+ public void init();
+
+ /**
+ * Disposes of this accessor - freeing all session resources
+ */
+ public void dispose();
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionAccessor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionAccessor.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionIdentifier.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionIdentifier.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionIdentifier.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionIdentifier.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.session;
+
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.safehaus.asyncweb.common.MutableHttpResponse;
+
+
+/**
+ * A strategy for encoding / decoding session keys information to / from
+ * requests.
+ *
+ * @author irvingd
+ *
+ */
+public interface HttpSessionIdentifier {
+
+ /**
+ * Attempts to extract a session key from a specified request.
+ *
+ * @param request The request from which to extract a session key
+ * @return The extracted key, or <code>null</code> if the request
+ * does not contain a session key
+ */
+ public String getSessionKey(HttpRequest request);
+
+ /**
+ * Adds a session key to the specified response
+ *
+ * @param key The session key
+ * @param response The response
+ */
+ public void addSessionKey(String key, MutableHttpResponse response);
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionIdentifier.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionIdentifier.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionKeyFactory.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionKeyFactory.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionKeyFactory.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionKeyFactory.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * A factory for creating Session keys.
+ * <code>SessionKeyFactory</code> implementations should make a best effort to:
+ * <ul>
+ * <li>Avoid the generation of two keys <i>k(1), k(2)</i>
+ * such that <i>k(1) == k(2)</i></li>
+ * <li>Avoid the generation of a key <i>k</i> such that an adversary can
+ * make an informed guess of the content of any other key created by this factory
+ * at any time in the future</li>
+ * </ul>
+ *
+ * @author irvingd
+ *
+ */
+public interface HttpSessionKeyFactory {
+
+ /**
+ * Returns a new session key String
+ *
+ * @return The session key
+ */
+ public String createSessionKey();
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionKeyFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionKeyFactory.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionListener.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionListener.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionListener.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionListener.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.session;
+
+import org.safehaus.asyncweb.service.HttpSession;
+
+/**
+ * Receives notifications of session lifecycle events
+ *
+ * @author irvingd
+ *
+ */
+public interface HttpSessionListener {
+
+ /**
+ * Invoked when a new session is created
+ *
+ * @param session The created session
+ */
+ public void sessionCreated(HttpSession session);
+
+ /**
+ * Invoked when a session is destroyed before it expires
+ *
+ * @param session The destroyed session
+ */
+ public void sessionDestroyed(HttpSession session);
+
+ /**
+ * Invoked when a session expires before being manually destroyed
+ *
+ * @param session The expired session
+ */
+ public void sessionExpired(HttpSession session);
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionListener.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionStore.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionStore.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionStore.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionStore.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,67 @@
+/*
+ * 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.service.HttpSession;
+
+
+/**
+ * Creates and maintains <code>Session</code>s.
+ *
+ * @author irvingd
+ *
+ */
+public interface HttpSessionStore {
+
+ /**
+ * Adds a listener to this <code>SessionStore</code>
+ *
+ * @param listener The listener to be added
+ */
+ public void addSessionListener(HttpSessionListener listener);
+
+ /**
+ * Closes this store, releasing any resources it may be consuming
+ */
+ public void close();
+
+ /**
+ * Creates a new session with a specified key.
+ * If the specified key is already in use, this method returns <code>null</code>
+ * to indicate that an alternative key should be used
+ *
+ * @param key The session key for the new session
+ * @return The created session, or <code>null</code> if the supplied key
+ * is already in use
+ */
+ public HttpSession createSession(String key);
+
+ /**
+ * Locates an existing session with the specified key.
+ * Any store which employs session time-outs should perform the appropriate
+ * action to mark the session as recently used before returning it.<br/>
+ *
+ * @param key The key for which a session is required
+ * @return The session, or <code>null</code> if no session was found with
+ * the specified key
+ */
+ public HttpSession locateSession(String key);
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionStore.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/HttpSessionStore.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/LoggingSessionListener.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/LoggingSessionListener.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/LoggingSessionListener.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/LoggingSessionListener.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,54 @@
+/*
+ * 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.service.HttpSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A simple <code>SessionListener</code> which logs the various lifecycle events
+ *
+ * @author irvingd
+ *
+ */
+public class LoggingSessionListener implements HttpSessionListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LoggingSessionListener.class);
+
+ public void sessionCreated(HttpSession session) {
+ doLog("New Session Created", session);
+ }
+
+ public void sessionDestroyed(HttpSession session) {
+ doLog("Session Destroyed", session);
+ }
+
+ public void sessionExpired(HttpSession session) {
+ doLog("Session Expired", session);
+ }
+
+ private void doLog(String msg, HttpSession session) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info(msg + " [SessionId = " + session.getId() + "]");
+ }
+ }
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/LoggingSessionListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/LoggingSessionListener.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/SecureRandomKeyFactory.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/SecureRandomKeyFactory.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/SecureRandomKeyFactory.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/SecureRandomKeyFactory.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,129 @@
+/*
+ * 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.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SecureRandomKeyFactory implements HttpSessionKeyFactory {
+
+ /**
+ * The default key length provided by this factory
+ */
+ private static final int DEFAULT_KEY_LENGTH = 16;
+
+ /**
+ * The minimum key length supported by this factory
+ */
+ private static final int MINIMUM_KEY_LENGTH = 8;
+
+ /**
+ * The default algorithm employed
+ */
+ private static final String DEFAULT_ALGORITHM = "SHA1PRNG";
+
+ private static final Logger LOG = LoggerFactory.getLogger(SecureRandomKeyFactory.class);
+
+ private SecureRandom secureRandom;
+ private int keyLength = DEFAULT_KEY_LENGTH;
+ private volatile boolean isStarted;
+ private String algorithm = DEFAULT_ALGORITHM;
+
+ /**
+ * Creates a session key based on bytes provided from an underlying
+ * <code>SecureRandom</code>
+ *
+ * @return The created key
+ */
+ public String createSessionKey() {
+ byte[] keyBytes = new byte[keyLength];
+ synchronized (secureRandom) {
+ secureRandom.nextBytes(keyBytes);
+ }
+ return bytesToSessionKey(keyBytes);
+ }
+
+ /**
+ * Sets the number of <i>bytes</i> used when forming keys created by this factory.
+ * A hex encoding is applied to the generated key bytes such that the number of
+ * <i>characters</i> in keys created by this factory is twice the key length
+ *
+ * @param keyLength The number of bytes employed in keys created by this factory
+ */
+ public void setKeyLength(int keyLength) {
+ if (keyLength < MINIMUM_KEY_LENGTH) {
+ throw new IllegalArgumentException("Key length must be >= " + MINIMUM_KEY_LENGTH);
+ }
+ if (isStarted) { // sanity check
+ throw new IllegalStateException("Key factory started");
+ }
+ this.keyLength = keyLength;
+ }
+
+ /**
+ * Sets the algorithm employed by the underlying <code>SecureRandom</code>.
+ * The default is <code>SHA1PRNG</code>
+ *
+ * @param algorithm The algorithm to be employed
+ */
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ /**
+ * Starts this factory.
+ */
+ public void start() {
+ isStarted = true;
+ LOG.info("Attempting to obtain SecureRandom using algorithim: " + algorithm);
+ try {
+ secureRandom = SecureRandom.getInstance(algorithm);
+ LOG.info("Ok - using algorithm: " + algorithm);
+ } catch (NoSuchAlgorithmException e) {
+ LOG.info("Failed to obtain secure random with algorithm: " + algorithm +
+ ". Resorting to default");
+ }
+ secureRandom = new SecureRandom();
+ secureRandom.nextBytes(new byte[keyLength]); // seed
+ }
+
+ private static String bytesToSessionKey(byte[] bytes) {
+ char[] keyChars = new char[bytes.length * 2];
+ for (int i = 0; i < bytes.length; ++i) {
+ byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
+ byte b2 = (byte) (bytes[i] & 0x0f);
+ keyChars[2 * i] = toKeyStringChar(b1);
+ keyChars[(2 * i) + 1] = toKeyStringChar(b2);
+ }
+ return new String(keyChars);
+ }
+
+ private static final char toKeyStringChar(byte b) {
+ if (b < 10) {
+ return (char) ('0' + b);
+ } else {
+ return (char) ('A' + (b - 10));
+ }
+ }
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/SecureRandomKeyFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/session/SecureRandomKeyFactory.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/DefaultHttpIoHandler.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/DefaultHttpIoHandler.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/DefaultHttpIoHandler.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/DefaultHttpIoHandler.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,60 @@
+/*
+ * 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.transport.mina;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.handler.multiton.SingleSessionIoHandler;
+import org.apache.mina.handler.multiton.SingleSessionIoHandlerDelegate;
+import org.apache.mina.handler.multiton.SingleSessionIoHandlerFactory;
+import org.safehaus.asyncweb.service.ServiceContainer;
+
+/**
+ * @author trustin
+ */
+public class DefaultHttpIoHandler extends SingleSessionIoHandlerDelegate implements HttpIoHandler {
+
+ public DefaultHttpIoHandler() {
+ super(new Factory());
+ }
+
+ public ServiceContainer getContainer() {
+ return ((Factory) getFactory()).getContainer();
+ }
+
+ public void setContainer(ServiceContainer container) {
+ ((Factory) getFactory()).setContainer(container);
+ }
+
+ private static class Factory implements SingleSessionIoHandlerFactory {
+ private ServiceContainer container;
+
+ public ServiceContainer getContainer() {
+ return container;
+ }
+
+ public void setContainer(ServiceContainer container) {
+ this.container = container;
+ }
+
+ public SingleSessionIoHandler getHandler(IoSession session) {
+ return new SingleHttpSessionIoHandler(container, session);
+ }
+ }
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/DefaultHttpIoHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/DefaultHttpIoHandler.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/HttpIoHandler.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/HttpIoHandler.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/HttpIoHandler.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/HttpIoHandler.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,34 @@
+/*
+ * 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.transport.mina;
+
+import org.apache.mina.common.IoHandler;
+import org.safehaus.asyncweb.service.ServiceContainer;
+
+public interface HttpIoHandler extends IoHandler {
+
+ /**
+ * Associates this handler with the container it should dispatch requests
+ * to
+ *
+ * @param container The associated container
+ */
+ void setContainer(ServiceContainer container);
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/HttpIoHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/HttpIoHandler.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/LoggingExceptionMonitor.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/LoggingExceptionMonitor.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/LoggingExceptionMonitor.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/LoggingExceptionMonitor.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.transport.mina;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.mina.common.ExceptionMonitor;
+
+/**
+ * An <code>ExceptionMonitor</code> which simply logs exceptions.
+ *
+ * @author irvingd
+ *
+ */
+class LoggingExceptionMonitor extends ExceptionMonitor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LoggingExceptionMonitor.class);
+
+ public void exceptionCaught(Throwable e) {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("NIOTransport encountered exception.", e);
+ }
+ }
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/LoggingExceptionMonitor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/LoggingExceptionMonitor.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/MinaTransport.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/MinaTransport.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/MinaTransport.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/MinaTransport.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,215 @@
+/*
+ * 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.transport.mina;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.mina.common.IoFilter;
+import org.apache.mina.filter.executor.ExecutorFilter;
+import org.apache.mina.filter.logging.LoggingFilter;
+import org.apache.mina.transport.socket.SocketSessionConfig;
+import org.apache.mina.transport.socket.nio.SocketAcceptor;
+import org.safehaus.asyncweb.service.ServiceContainer;
+import org.safehaus.asyncweb.service.Transport;
+import org.safehaus.asyncweb.service.TransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A <code>Transport</code> implementation which receives requests and sends
+ * responses using non-blocking selector based IO.
+ *
+ * @author irvingd
+ *
+ */
+public class MinaTransport implements Transport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MinaTransport.class);
+
+ private static final int DEFAULT_PORT = 9012;
+ private static final int DEFAULT_IO_THREADS = Runtime.getRuntime().availableProcessors();
+ private static final int DEFAULT_EVENT_THREADS = 16;
+
+ private SocketAcceptor acceptor;
+ private ExecutorService ioExecutor;
+ private ExecutorService eventExecutor;
+ private int port = DEFAULT_PORT;
+ private String address = null;
+ private int ioThreads = DEFAULT_IO_THREADS;
+ private int eventThreads = DEFAULT_EVENT_THREADS;
+ private HttpIoHandler ioHandler;
+ private boolean isLoggingTraffic = false;
+ private ServiceContainer container;
+
+ /**
+ * Sets the port this transport will listen on
+ *
+ * @param port The port
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * Sets the address this transport will listen on
+ *
+ * @param address The address to bind to.
+ * Specify <tt>null</tt> or <tt>"*"</tt> to listen to all
+ * NICs (Network Interface Cards).
+ */
+ public void setAddress(String address) {
+ if ("*".equals(address)) {
+ address = null;
+ }
+ this.address = address;
+ }
+
+ public int getIoThreads() {
+ return ioThreads;
+ }
+
+ /**
+ * Sets the number of worker threads employed by this transport.
+ * This should typically be a small number (2 is a good choice) -
+ * and is not tied to the number of concurrent connections you wish to
+ * support
+ *
+ * @param ioThreads The number of worker threads to employ
+ */
+ public void setIoThreads(int ioThreads) {
+ this.ioThreads = ioThreads;
+ }
+
+ public int getEventThreads() {
+ return eventThreads;
+ }
+
+ public void setEventThreads(int eventThreads) {
+ this.eventThreads = eventThreads;
+ }
+
+ /**
+ * Sets whether traffic received through this transport is
+ * logged (off by default)
+ *
+ * @param isLoggingTraffic <code>true</code> iff traffic should be logged
+ */
+ public void setIsLoggingTraffic(boolean isLoggingTraffic) {
+ this.isLoggingTraffic = isLoggingTraffic;
+ }
+
+ /**
+ * Sets the <code>ServiceContainer</code> to which we issue requests
+ *
+ * @param container Our associated <code>ServiceContainer</code>
+ */
+ public void setServiceContainer(ServiceContainer container) {
+ this.container = container;
+ }
+
+ /**
+ * Sets the <code>HttpIOHandler</code> to be employed by this transport
+ *
+ * @param httpIOHandler The handler to be employed by this transport
+ */
+ public void setIoHandler(HttpIoHandler httpIOHandler) {
+ this.ioHandler = httpIOHandler;
+ }
+
+ /**
+ * Starts this transport
+ *
+ * @throws TransportException If the transport can not be started
+ */
+ public void start() throws TransportException {
+ initIOHandler();
+ ioExecutor = new ThreadPoolExecutor(
+ ioThreads + 1, ioThreads + 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() );
+ eventExecutor = new ThreadPoolExecutor(
+ eventThreads + 1, eventThreads + 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() );
+ acceptor = new SocketAcceptor(ioThreads, ioExecutor);
+
+ try {
+ acceptor.getFilterChain().addLast(
+ "threadPool",
+ new ExecutorFilter(eventExecutor));
+ acceptor.setReuseAddress(true);
+ ((SocketSessionConfig) acceptor.getSessionConfig()).setReuseAddress(true);
+ if (isLoggingTraffic) {
+ LOG.info("Configuring traffic logging filter");
+ IoFilter filter = new LoggingFilter();
+ acceptor.getFilterChain().addFirst("LoggingFilter", filter);
+ }
+ acceptor.setBacklog(100);
+
+ if (address != null)
+ acceptor.setLocalAddress(new InetSocketAddress(address,port));
+ else
+ acceptor.setLocalAddress(new InetSocketAddress(port));
+ acceptor.setHandler(ioHandler);
+
+ acceptor.bind();
+
+ LOG.info("NIO HTTP Transport bound on port " + port);
+ } catch (IOException e) {
+ throw new TransportException("NIOTransport Failed to bind to port " + port, e);
+ }
+ }
+
+ /**
+ * Stops this transport
+ */
+ public void stop() throws TransportException {
+ acceptor.unbind();
+ ioExecutor.shutdown();
+ eventExecutor.shutdown();
+ }
+
+ /**
+ * @return A string representation of this transport
+ */
+ public String toString() {
+ return "NIOTransport [port=" + port + "]";
+ }
+
+ /**
+ * Initialises our handler - creating a new (default) handler if none has
+ * been specified
+ *
+ * @throws IllegalStateException If we have not yet been associated with a
+ * container
+ */
+ private void initIOHandler() {
+ if (ioHandler == null) {
+ LOG.info("No http IO Handler associated - using defaults");
+ ioHandler = new DefaultHttpIoHandler();
+ }
+ if (container == null) {
+ throw new IllegalStateException("Transport not associated with a container");
+ }
+ ioHandler.setContainer(container);
+ }
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/MinaTransport.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/MinaTransport.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/SingleHttpSessionIoHandler.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/SingleHttpSessionIoHandler.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/SingleHttpSessionIoHandler.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/SingleHttpSessionIoHandler.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,375 @@
+/*
+ * 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.transport.mina;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.apache.mina.common.DefaultWriteRequest;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoFutureListener;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.common.WriteRequest;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.codec.ProtocolDecoderException;
+import org.apache.mina.handler.multiton.SingleSessionIoHandler;
+import org.safehaus.asyncweb.codec.HttpServerCodecFactory;
+import org.safehaus.asyncweb.codec.decoder.HttpDecoderException;
+import org.safehaus.asyncweb.common.DefaultHttpRequest;
+import org.safehaus.asyncweb.common.DefaultHttpResponse;
+import org.safehaus.asyncweb.common.HttpRequest;
+import org.safehaus.asyncweb.common.HttpResponseStatus;
+import org.safehaus.asyncweb.common.HttpVersion;
+import org.safehaus.asyncweb.common.MutableHttpResponse;
+import org.safehaus.asyncweb.service.HttpServiceContext;
+import org.safehaus.asyncweb.service.HttpServiceFilter;
+import org.safehaus.asyncweb.service.ServiceContainer;
+import org.safehaus.asyncweb.service.context.AbstractHttpServiceContext;
+import org.safehaus.asyncweb.service.pipeline.RequestPipeline;
+import org.safehaus.asyncweb.service.pipeline.RequestPipelineListener;
+import org.safehaus.asyncweb.service.pipeline.StandardRequestPipeline;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+class SingleHttpSessionIoHandler implements SingleSessionIoHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SingleHttpSessionIoHandler.class);
+
+ /**
+ * The number of parsers we pre-allocate
+ */
+// private static final int DEFAULT_PARSERS = 5;
+
+ /**
+ * The default idle time
+ */
+ private static final int DEFAULT_IDLE_TIME = 30000;
+
+ /**
+ * Out default pipeline
+ */
+ private static final int DEFAULT_PIPELINE = 100;
+
+ private final ServiceContainer container;
+ private final IoSession session;
+ private final RequestPipeline pipeline;
+
+ private HttpServiceContext currentContext;
+ private int readIdleTime = DEFAULT_IDLE_TIME;
+
+ SingleHttpSessionIoHandler(ServiceContainer container, IoSession session) {
+ this.container = container;
+ this.session = session;
+ this.pipeline = new StandardRequestPipeline(DEFAULT_PIPELINE);
+
+ session.getConfig().setIdleTime(IdleStatus.READER_IDLE, readIdleTime);
+
+ session.getFilterChain().addLast(
+ "codec", new ProtocolCodecFilter(new HttpServerCodecFactory()));
+
+ session.getFilterChain().addLast(
+ "converter",
+ new ContextConverter());
+
+ session.getFilterChain().addLast(
+ "pipeline",
+ new RequestPipelineAdapter(pipeline));
+
+ int i = 0;
+ for (HttpServiceFilter serviceFilter: container.getServiceFilters()) {
+ session.getFilterChain().addLast(
+ "serviceFilter." + (i++),
+ new ServiceFilterAdapter(serviceFilter));
+ }
+ }
+
+ public void sessionCreated() {
+ }
+
+ public void sessionOpened() {
+ LOG.info("Connection opened");
+ }
+
+ public void sessionClosed() {
+ LOG.info("Connection closed");
+ }
+
+ /**
+ * Invoked when this connection idles out.
+ * If we are in the process of parsing a request, the current request
+ * is rejected with a {@link HttpResponseStatus#REQUEST_TIMEOUT} response status.
+ *
+ */
+ public void sessionIdle(IdleStatus idleType) {
+ if (session.getIdleCount(idleType) == 1) {
+// // FIXME currentRequest is always null now; we need to cooperate with a decoder.
+// if (currentContext != null) {
+// LOG.info("Read idled out while parsing request. Scheduling timeout response");
+// handleReadFailure(currentContext, HttpResponseStatus.REQUEST_TIMEOUT, "Timeout while reading request");
+// } else {
+ LOG.info("Idled with no current request. Scheduling closure when pipeline empties");
+ pipeline.runWhenEmpty(new Runnable() {
+ public void run() {
+ LOG.info("Pipeline empty after idle. Closing session");
+ session.close();
+ }
+ });
+// }
+ }
+ }
+
+ public void exceptionCaught(Throwable cause) {
+ MutableHttpResponse response = null;
+ if (cause instanceof ProtocolDecoderException) {
+ HttpResponseStatus status;
+ if (cause instanceof HttpDecoderException) {
+ status = ((HttpDecoderException) cause).getResponseStatus();
+ } else {
+ status = HttpResponseStatus.BAD_REQUEST;
+ }
+
+ LOG.warn("Bad request:", cause);
+
+ response = new DefaultHttpResponse();
+ response.setProtocolVersion(HttpVersion.HTTP_1_1);
+ response.setStatus(status);
+ } else if (cause instanceof IOException) {
+ LOG.warn("IOException on HTTP connection", cause);
+ session.close();
+ } else {
+ response = new DefaultHttpResponse();
+ response.setProtocolVersion(HttpVersion.HTTP_1_1);
+ response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
+ LOG.warn("Unexpected exception from a service.", cause);
+ }
+ if (response != null) {
+ HttpServiceContext context = this.currentContext;
+ if (context == null) {
+ context = createContext(new DefaultHttpRequest());
+ }
+ context.commitResponse(response);
+ }
+ }
+
+ public void messageReceived(Object message) {
+ // FIXME messageReceived invoked only when whole message is built.
+
+ // When headers were built
+ //sendContinuationIfRequested(request);
+
+ // When body has been built
+ }
+
+ /**
+ * Sends a continuation response for the specified request, if
+ * the client has requested that a continuation response should
+ * be provided.</br>
+ * The continuation response is enqueued with our pipe-line.
+ * Note that we do <i>not</i> commit the request - a final response
+ * must still be provided.
+ *
+ * TODO: We're not currently adding value here: If we cant route
+ * to a service, we'll still accept the request. We should
+ * add the ability to inject the request in to the container
+ * and let it decide whether the request can be handled.
+ */
+ private void sendContinuationIfRequested(HttpServiceContext context) {
+ if (context.getRequest().requiresContinuationResponse()) {
+ MutableHttpResponse continuationResponse = new DefaultHttpResponse();
+ continuationResponse.setStatus(HttpResponseStatus.CONTINUE);
+ context.commitResponse(continuationResponse);
+ LOG.info("Scheduled continuation response");
+ }
+ }
+
+ /**
+ * Invoked when we fail to parse an incoming request.
+ * We configure our parser to discard any further data received from the client,
+ * and schedule a response with the appropriate failure code for the
+ * current request
+ *
+ * @param status The status
+ * @param message Failure message
+ */
+ private void handleReadFailure(HttpServiceContext context, HttpResponseStatus status, String message) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("Failed to handle client request. Reason: " + status);
+ }
+ MutableHttpResponse response = new DefaultHttpResponse();
+ response.setStatusReasonPhrase(message);
+ response.setStatus(status);
+ context.commitResponse(response);
+ }
+
+ /**
+ * Invoked when data wrote has been fully written.
+ * If we have scheduled closure after sending a final response, we will
+ * be provided with the <code>CLOSE_MARKER</code> as our marker object.<br/>
+ * This signals us to schedule closure of the connection
+ *
+ * @param message The marker provided when writing data. If this is
+ * our closure marker, we schedule closure of the connection
+ */
+ public void messageSent(Object message) {
+ }
+
+ /**
+ * Sets the read idle time for all connections
+ *
+ * @param readIdleTime The read idle time (seconds)
+ */
+ public void setReadIdleTime(int readIdleTime) {
+ this.readIdleTime = readIdleTime;
+ }
+
+ private HttpServiceContext createContext(HttpRequest request) {
+ return new DefaultHttpServiceContext(request);
+ }
+
+ private class ContextConverter extends IoFilterAdapter {
+
+ @Override
+ public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
+ nextFilter.filterWrite(
+ session,
+ new DefaultWriteRequest(
+ ((HttpServiceContext) writeRequest.getMessage()).getCommittedResponse(),
+ writeRequest.getFuture()));
+ }
+
+ @Override
+ public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
+ HttpRequest request = (HttpRequest) message;
+ HttpServiceContext context = createContext(request);
+ currentContext = context;
+ nextFilter.messageReceived(session, context);
+ }
+ }
+
+ private class ServiceFilterAdapter extends IoFilterAdapter {
+ private final HttpServiceFilter filter;
+
+ public ServiceFilterAdapter(HttpServiceFilter filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public void messageReceived(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception {
+ org.safehaus.asyncweb.service.HttpServiceFilter.NextFilter nextFilterAdapter =
+ new org.safehaus.asyncweb.service.HttpServiceFilter.NextFilter() {
+ public void invoke() {
+ nextFilter.messageReceived(session, message);
+ }
+ };
+ filter.handleRequest(nextFilterAdapter, (HttpServiceContext) message);
+ }
+
+ @Override
+ public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) throws Exception {
+ org.safehaus.asyncweb.service.HttpServiceFilter.NextFilter nextFilterAdapter =
+ new org.safehaus.asyncweb.service.HttpServiceFilter.NextFilter() {
+ public void invoke() {
+ nextFilter.filterWrite(session, writeRequest);
+ }
+ };
+
+ HttpServiceContext context = (HttpServiceContext) writeRequest.getMessage();
+
+ filter.handleResponse(nextFilterAdapter, context);
+ }
+ }
+
+ private class RequestPipelineAdapter extends IoFilterAdapter {
+
+ private final RequestPipeline pipeline;
+
+ public RequestPipelineAdapter(final RequestPipeline pipeline) {
+ this.pipeline = pipeline;
+ }
+
+ public void sessionOpened(final NextFilter nextFilter, final IoSession session) {
+ pipeline.setPipelineListener(new RequestPipelineListener() {
+ public void responseReleased(HttpServiceContext context) {
+ nextFilter.filterWrite(
+ session,
+ new DefaultWriteRequest(
+ context, ((DefaultHttpServiceContext) context).getWriteFuture()));
+ }
+ });
+
+ nextFilter.sessionOpened(session);
+ }
+
+ @Override
+ public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
+ HttpServiceContext context = (HttpServiceContext) message;
+ if (pipeline.addRequest(context)) {
+ LOG.debug("Allocated slot in request pipeline");
+ nextFilter.messageReceived(session, message);
+ } else {
+ // The client has filled their pipeline. Currently, this
+ // triggers closure. Another option would be to drop read interest
+ // until we drain.
+ LOG.warn("Could not allocate room in the pipeline for request");
+ handleReadFailure(context, HttpResponseStatus.SERVICE_UNAVAILABLE, "Pipeline full");
+ }
+ }
+
+ @Override
+ public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
+ DefaultHttpServiceContext context = (DefaultHttpServiceContext) writeRequest.getMessage();
+ context.setWriteFuture(writeRequest.getFuture());
+ pipeline.releaseResponse(context);
+ // nextFilter will be invoked when pipeline listener is notified.
+ }
+ }
+
+ private class DefaultHttpServiceContext extends AbstractHttpServiceContext {
+ private WriteFuture writeFuture;
+
+ private DefaultHttpServiceContext(HttpRequest request) {
+ super((InetSocketAddress) session.getRemoteAddress(), request, container);
+ }
+
+ private WriteFuture getWriteFuture() {
+ return writeFuture;
+ }
+
+ private void setWriteFuture(WriteFuture writeFuture) {
+ if (!isResponseCommitted()) {
+ throw new IllegalStateException();
+ }
+ this.writeFuture = writeFuture;
+ }
+
+ @Override
+ protected void doWrite(boolean requiresClosure) {
+ currentContext = null;
+ WriteFuture future = session.write(this);
+ if (requiresClosure) {
+ LOG.debug("Added CLOSE future listener.");
+ future.addListener(IoFutureListener.CLOSE);
+ }
+ }
+ }
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/SingleHttpSessionIoHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/service/transport/mina/SingleHttpSessionIoHandler.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpDateFormat.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpDateFormat.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpDateFormat.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpDateFormat.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,98 @@
+/*
+ * 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.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Utility for generating date strings in the format required by HTTP.
+ *
+ * @author irvingd
+ *
+ */
+public class HttpDateFormat {
+
+ /**
+ * By default, we update the format if it is more than a second old
+ */
+ private static final int DEFAULT_GRANULARITY = 1000;
+
+ private static int granularity = DEFAULT_GRANULARITY;
+
+ /**
+ * Thread local <code>HttpDateFormat</code>
+ */
+ private static final ThreadLocal FORMAT_LOCAL = new ThreadLocal() {
+ protected Object initialValue() {
+ return new HttpDateFormat();
+ }
+ };
+
+ /**
+ * Format for HTTP dates
+ */
+ private final SimpleDateFormat dateFormat =
+ new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+ /**
+ * The time of the last format operation (0 if none have yet taken place)
+ */
+ private long timeLastGenerated;
+
+ /**
+ * The current formatted HTTP date
+ */
+ private String currentHTTPDate;
+
+ private HttpDateFormat() {
+ // HTTP date format specifies GMT
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
+ /**
+ * Returns the current time formatted as specified in the HTTP 1.1 specification.
+ *
+ * @return The formatted date
+ */
+ public static String getCurrentHttpDate() {
+ return ((HttpDateFormat) FORMAT_LOCAL.get()).getCurrentDate();
+ }
+
+ /**
+ * Provides the current formatted date to be employed.
+ * If we haven't updated our view of the time in the last 'granularity' ms,
+ * we format a fresh value.
+ *
+ * @return The current http date
+ */
+ private String getCurrentDate() {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - timeLastGenerated > granularity) {
+ timeLastGenerated = currentTime;
+ currentHTTPDate = dateFormat.format(new Date(currentTime));
+ }
+ return currentHTTPDate;
+ }
+
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpDateFormat.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpDateFormat.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpHeaderConstants.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpHeaderConstants.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpHeaderConstants.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpHeaderConstants.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,78 @@
+/*
+ * 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.util;
+
+/**
+ * HTTP Header Constants.
+ *
+ * @author irvingd
+ * @author trustin
+ * @version $Rev$, $Date$
+ */
+public class HttpHeaderConstants {
+
+ /**
+ * The name of the "connection" header
+ */
+ public static final String KEY_CONNECTION = "Connection";
+
+ /**
+ * The server header
+ */
+ public static final String KEY_SERVER = "Server";
+
+ /**
+ * The header value to indicate connection closure
+ */
+ public static final String VALUE_CLOSE = "close";
+
+ /**
+ * The header value to indicate connection keep-alive (http 1.0)
+ */
+ public static final String VALUE_KEEP_ALIVE = "Keep-Alive";
+
+ /**
+ * The "content-length" header name
+ */
+ public static final String KEY_CONTENT_LENGTH = "Content-Length";
+
+ /**
+ * The "transfer-coding" header name
+ */
+ public static final String KEY_TRANSFER_CODING = "Transfer-Coding";
+
+ /**
+ * The "expect" header name
+ */
+ public static final String KEY_EXPECT = "Expect";
+
+ /**
+ * The continue expectation
+ */
+ public static final String VALUE_CONTINUE_EXPECTATION = "100-continue";
+
+ /**
+ * The "date" header
+ */
+ public static final String KEY_DATE = "Date";
+
+ private HttpHeaderConstants() {
+ }
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpHeaderConstants.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/HttpHeaderConstants.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/LinkedPermitIssuer.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/LinkedPermitIssuer.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/LinkedPermitIssuer.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/LinkedPermitIssuer.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,380 @@
+/*
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A <code>TimedPermitIssuer</code> which stores all issued permits in a linked
+ * list.
+ * As a permit is renewed, its lifetime is extended and it is simply moved to the
+ * back of the list (As <code>LinkedPermitIssuer</code> uses a fixed lifetime for
+ * all permits and renewals).<br/>
+ * Each permit issued by this issuer has direct access to its place in the list -
+ * allowing constant time renewals.
+ *
+ * @author irvingd
+ *
+ */
+public class LinkedPermitIssuer implements TimedPermitIssuer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LinkedPermitIssuer.class);
+
+ /**
+ * The head of the permit list.
+ * This entry is the next due to expire
+ */
+ private PermitEntry head;
+
+ /**
+ * The tail of the permit list
+ */
+ private PermitEntry tail;
+
+ /**
+ * The lifetime given to new permits, and permit renewals
+ */
+ private long lifetime;
+
+ private Object lock = new Object();
+
+ private List<PermitExpirationListener> listeners = Collections.synchronizedList(new ArrayList<PermitExpirationListener>());
+
+ private boolean isClosed;
+
+ /**
+ * Creates a <code>LinkedPermitIssuer</code> with a specified lifetime given to
+ * new permits. When a permit issued by this issuer is renewed, its expiry time
+ * is renewed to the current time plus this lifetime
+ *
+ * @param lifetime The lifetime to be used for new permits, and permit renewals
+ */
+ public LinkedPermitIssuer(long lifetime) {
+ if (lifetime <= 0) {
+ throw new IllegalArgumentException("lifetime must be >0");
+ }
+ this.lifetime = lifetime;
+ new Thread(new ExpiryNotifier()).start();
+ }
+
+ /**
+ * Issues a new <code>TimedPermit</code> for the target object.
+ * Unless <code>renew</code>ed, the permit expires after this
+ * issuers imposed lifetime. Upon renewal, the permit becomes valid
+ * for this issuers configured lifetime from the time of the renewal.
+ *
+ * @param o The target object
+ */
+ public TimedPermit issuePermit(Object o) {
+ PermitEntry permit;
+ synchronized (lock) {
+ permit = new PermitEntry(o);
+ if (isEmpty()) {
+ head = tail = permit;
+ lock.notify(); // notify when move from empty to non empty
+ } else {
+ tail.entryAfter = permit;
+ permit.entryBefore = tail;
+ tail = permit;
+ }
+ }
+ return permit;
+ }
+
+ /**
+ * Adds a <code>PermitExpirationListener</code> to this issuer
+ *
+ * @param listener the listener
+ */
+ public void addPermitExpirationListener(PermitExpirationListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Closes this issuer
+ */
+ public void close() {
+ synchronized (lock) {
+ isClosed = true;
+ lock.notify();
+ LOG.debug("Marked as closed");
+ }
+ }
+
+ /**
+ * Determines whether there are any outstanding permits
+ * to be processed
+ *
+ * @return <code>true</code> if there are no outstanding
+ * permits
+ */
+ private boolean isEmpty() {
+ return head == null;
+ }
+
+ /**
+ * Moves a <code>PermitEntry</code> to the back of the list.
+ * An entry is moved to the back upon renewal. If we move the current head
+ * to the back, we notify to allow the expiry time of the next element
+ * to be observed
+ *
+ * @param entry The entry to move
+ */
+ private void moveToBack(PermitEntry entry) {
+ boolean movedHead = (entry == head);
+ if (entry != tail) { // nothing to move / no need to notify if already at back
+ PermitEntry previous = entry.entryBefore;
+ PermitEntry after = entry.entryAfter;
+
+ tail.entryAfter = entry;
+ entry.entryBefore = tail;
+ entry.entryAfter = null;
+ tail = entry;
+
+ after.entryBefore = previous;
+ if (!movedHead) {
+ previous.entryAfter = after;
+ } else {
+ head = after;
+ lock.notify();
+ }
+ }
+ }
+
+ /**
+ * Removes the head entry (without notifications)
+ */
+ private void removeHead() {
+ head = head.entryAfter;
+ if (head != null) {
+ head.entryBefore = null;
+ } else {
+ LOG.debug("Permit list empty following removal");
+ }
+ }
+
+ /**
+ * Notifies all listeners of the expiry of a specified target object
+ *
+ * @param target The expired target object
+ */
+ private void notifyExpiry(Object target) {
+ synchronized (listeners) {
+ for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
+ PermitExpirationListener listener = (PermitExpirationListener) iter.next();
+ listener.permitExpired(target);
+ }
+ }
+ }
+
+ /**
+ * An entry in a linked list of permits.
+ *
+ * @author irvingd
+ *
+ */
+ private class PermitEntry implements TimedPermit {
+
+ private PermitEntry entryBefore;
+ private PermitEntry entryAfter;
+ private long expiryTime;
+ private Object o;
+ private boolean isCancelled;
+
+ PermitEntry(Object o) {
+ this.o = o;
+ extendLifetime();
+ }
+
+ /**
+ * Renews this permit. If we are cancelled, no action is taken.
+ * Otherwise, our lifetime is extended, and we move to the back of the
+ * list
+ */
+ public void renew() {
+ synchronized (lock) {
+ if (!isCancelled) {
+ extendLifetime();
+ moveToBack(this);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entry has been renewed. New expiry time: " + expiryTime);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return This permits target object
+ */
+ Object getTarget() {
+ return o;
+ }
+
+ /**
+ * @return the time remaining, in ms until this permit
+ * expires
+ */
+ long timeToExpiry() {
+ return expiryTime - System.currentTimeMillis();
+ }
+
+ /**
+ * @return <code>true</code> if this permit has been cancelled and should be
+ * removed
+ */
+ boolean isCancelled() {
+ return isCancelled;
+ }
+
+ /**
+ * Cancels this permit.
+ * If we are either expired or already cancelled, no action is taken.
+ * Otherwise, we mark ourself as cancelled.
+ * If we are at the head of the permit list, we notify to allow
+ * the notification thread to take any required action.
+ */
+ public boolean cancel() {
+ synchronized (lock) {
+ if (isCancelled) {
+ LOG.debug("Ignoring cancel request");
+ return false;
+ }
+ isCancelled = true;
+ LOG.debug("Entry has been successfully cancelled");
+ if (this == head) {
+ LOG.debug("Head entry cancelled - notifying");
+ lock.notify();
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Marks this permit as cancelled
+ */
+ void markCancelled() {
+ isCancelled = true;
+ }
+
+ /**
+ * Extends our lifetime
+ */
+ private void extendLifetime() {
+ expiryTime = System.currentTimeMillis() + lifetime;
+ }
+
+ }
+
+ /**
+ * Services the head of the permit list, blocking until work is available.
+ * Cancelled and expired entries are removed (listeners are notified of all
+ * expired entries)
+ *
+ * @author irvingd
+ */
+ private class ExpiryNotifier implements Runnable {
+
+ public void run() {
+ try {
+ LOG.debug("ExpiryNotifier starting");
+ while (processHeadEntry()) {
+ continue;
+ }
+ LOG.debug("ExpiryNotifier closing");
+ } catch (RuntimeException e) {
+ LOG.error("Unexpected exception on expiry notifier", e);
+ }
+
+ }
+
+ /**
+ * Waits for cancellation / expiration of the head entry.
+ * If the head entry is expired, listeners are notified.
+ *
+ * @return <code>true</code> if the entry is processed,
+ * <code>false</code> if we become closed while waiting for a result
+ */
+ private boolean processHeadEntry() {
+ PermitEntry toExpire = null;
+ try {
+ synchronized (lock) {
+ while (!isClosed && isEmpty()) {
+ lock.wait();
+ }
+ if (isClosed) {
+ return false;
+ } else {
+ toExpire = processFirst();
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (toExpire != null) {
+ notifyExpiry(toExpire.getTarget());
+ }
+ return true;
+ }
+
+ /**
+ * Examines the head of the list for cancellation or expiry.
+ * We block until either the entry is cancelled, expires, or we are
+ * closed
+ *
+ * @return The previous head entry - if it has expired.
+ * <code>null</code> if the entry was cancelled, or we were closed
+ */
+ private PermitEntry processFirst() {
+ PermitEntry entry = head;
+ boolean expired = false;
+ long timeToExpiry = entry.timeToExpiry();
+ while (!isClosed && !entry.isCancelled() && timeToExpiry > 0) {
+ try {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Waiting for head entry to expire: " + timeToExpiry + "ms");
+ }
+ lock.wait(timeToExpiry);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Unexpected interrupt");
+ }
+ timeToExpiry = entry.timeToExpiry();
+ }
+
+ if (entry.isCancelled()) {
+ LOG.debug("Head entry is cancelled. Removing");
+ removeHead();
+ } else if (!isClosed) {
+ LOG.debug("Head entry has expired");
+ entry.markCancelled();
+ removeHead();
+ expired = true;
+ }
+ return expired ? entry : null;
+ }
+
+ }
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/LinkedPermitIssuer.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/LinkedPermitIssuer.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/PermitExpirationListener.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/PermitExpirationListener.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/PermitExpirationListener.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/PermitExpirationListener.java Mon Oct 22 19:29:38 2007
@@ -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 org.safehaus.asyncweb.util;
+
+/**
+ * Something interested in receiving notifications when permits issued by a
+ * <code>TimedPermitIssuer</code> exipre
+ *
+ * @author irvingd
+ *
+ */
+public interface PermitExpirationListener {
+
+ /**
+ * Invoked when the permit associated with the specified object expires
+ *
+ * @param o The object for which an associated permit has expired
+ */
+ public void permitExpired(Object o);
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/PermitExpirationListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/PermitExpirationListener.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/StringBundle.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/StringBundle.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/StringBundle.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/StringBundle.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,108 @@
+/*
+ * 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.util;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simplifies looking up locale specific format strings.
+ *
+ * @author irvingd
+ *
+ */
+public class StringBundle {
+
+ private static final String RESOURCE_POSTFIX = ".strings";
+ private static final Logger LOG = LoggerFactory.getLogger(StringBundle.class);
+ private static Map<String, StringBundle> bundles = new HashMap<String, StringBundle>();
+
+ private ResourceBundle bundle;
+
+ private StringBundle(String packageName) {
+ String bundleName = packageName + RESOURCE_POSTFIX;
+ bundle = ResourceBundle.getBundle(bundleName);
+ if (bundle == null) {
+ LOG.warn("Cant find resource '" + bundleName + "'");
+ }
+ }
+
+ public synchronized static StringBundle getBundle(String packageName) {
+ StringBundle bundle = bundles.get(packageName);
+ if (bundle == null) {
+ bundle = new StringBundle(packageName);
+ bundles.put(packageName, bundle);
+ }
+ return bundle;
+ }
+
+ public String getString(String key) {
+ return MessageFormat.format(getValue(key), (Object) null);
+ }
+
+ public String getString(String key, Object param) {
+ Object[] params = new Object[] {param};
+ return getString(key, params);
+ }
+
+ public String getString(String key, Object param1, Object param2) {
+ Object[] params = new Object[] {param1, param2};
+ return getString(key, params);
+ }
+
+ public String getString(String key, Object[] params) {
+ String value = getValue(key);
+ String formatted;
+ try {
+ formatted = MessageFormat.format(value, params);
+ } catch (IllegalArgumentException e) {
+ formatted = applyDefaultFormat(value, params);
+ }
+ return formatted;
+ }
+
+ private String getValue(String key) {
+ if (key == null) {
+ throw new IllegalArgumentException("Null key");
+ }
+ if (bundle == null) {
+ return key;
+ }
+ String value = bundle.getString(key);
+ return value == null ? "Missing resource '" + key + "'" : value;
+ }
+
+ private String applyDefaultFormat(String value, Object[] params) {
+ StringBuffer buff = new StringBuffer();
+ buff.append(value);
+ if (params != null) {
+ for (int i=0; i<params.length; ++i) {
+ buff.append(" param[").append(i).append(" = ").append(params[i]).append(" ]");
+ }
+ }
+ return buff.toString();
+ }
+
+}
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/StringBundle.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/StringBundle.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added: mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/TimedPermit.java
URL: http://svn.apache.org/viewvc/mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/TimedPermit.java?rev=587364&view=auto
==============================================================================
--- mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/TimedPermit.java (added)
+++ mina/sandbox/asyncweb/core/src/main/java/org/safehaus/asyncweb/util/TimedPermit.java Mon Oct 22 19:29:38 2007
@@ -0,0 +1,56 @@
+/*
+ * 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.util;
+
+/**
+ * A time limited permit granting access to a target object.
+ * A <code>TimedPermit</code> is issued for a target object by a <code>TimedPermitIssue</code>.
+ * When the time limit (determined by the issuer) is reached, any listeners attached to
+ * the issuer are notified.
+ *
+ * @author irvingd
+ *
+ */
+public interface TimedPermit {
+
+ /**
+ * Extends the lifetime of this permit.
+ * This is typically used as a "keep-alive" mechanism. For example, if a permit is
+ * issued to manage the idle expiry time of an http session, the permit might be
+ * extended each time the client issues a request associated with the session.<br/>
+ * The amount of time added to the lifetime of this permit by invoking this method
+ * is determined by the <code>TimedPermitIssuer</code> which issued this permit.
+ *
+ * Invoking this method has no effect if this permit is already expired
+ */
+ public void renew();
+
+ /**
+ * Cancels this permit. After invoking this method, it is guaranteed that
+ * no expiry notification will be made for the target object by this permits
+ * associated <code>TimedPermitIssuer</code>.
+ *
+ * @return <code>true</code> if invoking this method prevented expiry notification
+ * from taking place, <code>false</code> if expiry notification has already
+ * taken place
+ */
+ public boolean cancel();
+
+}