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/11/14 04:51:39 UTC

svn commit: r594745 - /mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/

Author: trustin
Date: Tue Nov 13 19:51:36 2007
New Revision: 594745

URL: http://svn.apache.org/viewvc?rev=594745&view=rev
Log:
Rewrote KeepAliveFilter which is more flexible (I hope)

Added:
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilter.java   (with props)
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveMessageFactory.java   (with props)
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAlivePolicy.java   (with props)
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveTimeoutException.java   (with props)
Removed:
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/AbstractKeepAliveFilter.java
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/ActiveKeepAliveFilter.java
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilterFactory.java
    mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/PassiveKeepAliveFilter.java

Added: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilter.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilter.java?rev=594745&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilter.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilter.java Tue Nov 13 19:51:36 2007
@@ -0,0 +1,338 @@
+/*
+ *  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.apache.mina.filter.keepalive;
+
+import org.apache.mina.common.AttributeKey;
+import org.apache.mina.common.DefaultWriteRequest;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoFilter;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoFilterChain;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.IoSessionConfig;
+import org.apache.mina.common.IoSessionLogger;
+import org.apache.mina.common.WriteRequest;
+import org.slf4j.Logger;
+
+/**
+ * An {@link IoFilter} that sends a keep-alive request on
+ * <tt>sessionIdle</tt> event ({@link IdleStatus#READER_IDLE}) and sends the
+ * response for the keep-alive request.
+ * 
+ * <h2>Interference with {@link IoSessionConfig#setIdleTime(IdleStatus, int)}</h2>
+ * 
+ * This filter adjusts <tt>idleTime</tt> property for
+ * {@link IdleStatus#READER_IDLE} automatically.  Changing the <tt>idleTime</tt>
+ * property for {@link IdleStatus#READER_IDLE} will lead this filter to a
+ * unexpected behavior.
+ * 
+ * <h2>Implementing {@link KeepAliveMessageFactory}</h2>
+ * 
+ * To use this filter, you have to provide an implementation of
+ * {@link KeepAliveMessageFactory}, which determines a received or sent
+ * message is a keep-alive message or not and creates a new keep-alive
+ * message:
+ * 
+ * <table>
+ * <tr>
+ * <th>Case name</th><th>Case description</th><th>Implementation</th>
+ * </tr>
+ * <tr>
+ * <td>Active</td>
+ * <td>You want a keep-alive request is sent when the reader is idle.
+ * Once the request is sent, the response for the request should be
+ * received within <tt>keepAliveTimeout</tt> seconds.  Otherwise, the specified
+ * {@link KeepAlivePolicy} should be enforced.  If a keep-alive request
+ * is received, its response also should be sent back.</td>
+ * <td>Both {@link KeepAliveMessageFactory#getRequest(IoSession)} and
+ * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must
+ * return a non-<tt>null</tt>.</td>
+ * </tr>
+ * <tr>
+ * <td>Semi-active</td>
+ * <td>You want a keep-alive request to be sent when the reader is idle.
+ * However, you don't really care if the response is received or not.
+ * If a keep-alive request is received, its response should
+ * also be sent back.
+ * </td>
+ * <td>Both {@link KeepAliveMessageFactory#getRequest(IoSession)} and
+ * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must
+ * return a non-<tt>null</tt>, and the <tt>policy</tt> property
+ * should be set to {@link KeepAlivePolicy#OFF} or {@link KeepAlivePolicy#LOG}.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>Passive</td>
+ * <td>You don't want to send a keep-alive request by yourself, but the
+ * response should be sent back if a keep-alive request is received.</td>
+ * <td>{@link KeepAliveMessageFactory#getRequest(IoSession)} must return
+ * <tt>null</tt> and {@link KeepAliveMessageFactory#getResponse(IoSession, Object)}
+ * must return a non-<tt>null</tt>.</td>
+ * </tr>
+ * <tr>
+ * <td>Deaf Speaker</td>
+ * <td>You want a keep-alive request to be sent when the reader is idle, but
+ * you don't want to send any response back.</td> 
+ * <td>{@link KeepAliveMessageFactory#getRequest(IoSession)} must return
+ * a non-<tt>null</tt> and
+ * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must
+ * return <tt>null</tt>.</td>
+ * </tr>
+ * <tr>
+ * <td>Silent Listener</td>
+ * <td>You don't want to send a keep-alive request by yourself nor send any
+ * response back.</td> 
+ * <td>Both {@link KeepAliveMessageFactory#getRequest(IoSession)} and
+ * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must
+ * return <tt>null</tt>.</td>
+ * </tr>
+ * </table>
+ * Please note that you must implement
+ * {@link KeepAliveMessageFactory#isRequest(IoSession, Object)} and
+ * {@link KeepAliveMessageFactory#isResponse(IoSession, Object)} properly
+ * whatever case you chose.
+ * 
+ * <h2>Enforcing a policy</h2>
+ * 
+ * You can enforce a predefined policy by specifying a {@link KeepAlivePolicy}.
+ * The default policy is {@link KeepAlivePolicy#CLOSE}.
+ *  
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class KeepAliveFilter extends IoFilterAdapter {
+    
+    private final AttributeKey WAITING_FOR_PONG = new AttributeKey(
+            getClass(), "waitingForPong");
+
+    private final KeepAliveMessageFactory messageFactory;
+    private volatile KeepAlivePolicy policy;
+    private volatile int keepAliveRequestInterval;
+    private volatile int keepAliveRequestTimeout;
+    
+    /**
+     * Creates a new instance with the default {@link KeepAlivePolicy} and
+     * the default timeout values (<tt>policy</tt> =
+     * {@link KeepAlivePolicy#CLOSE}, <tt>keepAliveRequestInterval = 60</tt>
+     * and <tt>keepAliveRequestTimeout = 30</tt>).
+     */
+    public KeepAliveFilter(KeepAliveMessageFactory messageFactory) {
+        this(messageFactory, KeepAlivePolicy.CLOSE);
+    }
+    
+    /**
+     * Creates a new instance with the default timeout values
+     * (<tt>keepAliveRequestInterval = 60</tt> and
+     * <tt>keepAliveRequestTimeout = 30</tt>).
+     */
+    public KeepAliveFilter(
+            KeepAliveMessageFactory messageFactory, KeepAlivePolicy policy) {
+        this(messageFactory, policy, 60, 30);
+    }
+
+    /**
+     * Creates a new instance.
+     */
+    public KeepAliveFilter(
+            KeepAliveMessageFactory messageFactory, KeepAlivePolicy policy,
+            int keepAliveRequestInterval, int keepAliveRequestTimeout) {
+        if (messageFactory == null) {
+            throw new NullPointerException("messageFactory");
+        }
+        if (policy == null) {
+            throw new NullPointerException("policy");
+        }
+        
+        this.messageFactory = messageFactory;
+        this.policy = policy;
+        
+        setKeepAliveRequestInterval(keepAliveRequestInterval);
+        setKeepAliveRequestTimeout(keepAliveRequestTimeout);
+    }
+
+    public KeepAlivePolicy getPolicy() {
+        return policy;
+    }
+
+    public void setPolicy(KeepAlivePolicy policy) {
+        if (policy == null) {
+            throw new NullPointerException("policy");
+        }
+        this.policy = policy;
+    }
+
+    public int getKeepAliveRequestInterval() {
+        return keepAliveRequestInterval;
+    }
+
+    public void setKeepAliveRequestInterval(int keepAliveRequestInterval) {
+        if (keepAliveRequestInterval <= 0) {
+            throw new IllegalArgumentException(
+                    "keepAliveRequestInterval must be a positive integer: " +
+                    keepAliveRequestInterval);
+        }
+        this.keepAliveRequestInterval = keepAliveRequestInterval;
+    }
+
+    public int getKeepAliveRequestTimeout() {
+        return keepAliveRequestTimeout;
+    }
+
+    public void setKeepAliveRequestTimeout(int keepAliveRequestTimeout) {
+        if (keepAliveRequestTimeout <= 0) {
+            throw new IllegalArgumentException(
+                    "keepAliveRequestTimeout must be a positive integer: " +
+                    keepAliveRequestTimeout);
+        }
+        this.keepAliveRequestTimeout = keepAliveRequestTimeout;
+    }
+
+    public KeepAliveMessageFactory getMessageFactory() {
+        return messageFactory;
+    }
+
+    @Override
+    public void onPreAdd(IoFilterChain parent, String name,
+            NextFilter nextFilter) throws Exception {
+        if (parent.contains(this)) {
+            throw new IllegalArgumentException(
+                    "You can't add the same filter instance more than once. " +
+                    "Create another instance and add it.");
+        }
+    }
+
+    @Override
+    public void onPostAdd(
+            IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
+        resetStatus(parent.getSession());
+    }
+
+    @Override
+    public void onPostRemove(
+            IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
+        resetStatus(parent.getSession());
+    }
+
+    @Override
+    public void messageReceived(
+            NextFilter nextFilter, IoSession session, Object message) throws Exception {
+        try {
+            if (messageFactory.isRequest(session, message)) {
+                Object pongMessage =
+                    messageFactory.getResponse(session, message);
+                
+                if (pongMessage != null) {
+                    nextFilter.filterWrite(
+                            session, new DefaultWriteRequest(pongMessage));
+                }
+            }
+            
+            if (messageFactory.isResponse(session, message)) {
+                resetStatus(session);
+            }
+        } finally {
+            if (!isKeepAliveMessage(session, message)) {
+                nextFilter.messageReceived(session, message);
+            }
+        }
+    }
+
+    @Override
+    public void messageSent(
+            NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
+        Object message = writeRequest.getMessage();
+        if (!isKeepAliveMessage(session, message)) {
+            nextFilter.messageSent(session, writeRequest);
+        }
+    }
+
+    @Override
+    public void sessionIdle(
+            NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {
+        try {
+            if (status == IdleStatus.READER_IDLE) {
+                if (!session.containsAttribute(WAITING_FOR_PONG)) {
+                    Object pingMessage = messageFactory.getRequest(session);
+                    if (pingMessage != null) {
+                        // The first idleness.
+                        nextFilter.filterWrite(
+                                session,
+                                new DefaultWriteRequest(pingMessage));
+                        markStatus(session);
+                    }
+                } else {
+                    resetStatus(session);
+                    // DO something based on policy.
+                    switch (getPolicy()) {
+                    case OFF:
+                        break;
+                    case LOG:
+                        logTimeout(session);
+                        break;
+                    case EXCEPTION:
+                        throw new KeepAliveTimeoutException(
+                                getTimeoutMessage());
+                    case CLOSE:
+                        logTimeout(session);
+                        session.close();
+                        break;
+                    default:
+                        throw new InternalError();
+                    }
+                }
+            }
+        } finally {
+            nextFilter.sessionIdle(session, status);
+        }
+    }
+
+    private void logTimeout(IoSession session) {
+        Logger logger = IoSessionLogger.getLogger(session, getClass());
+        if (logger.isWarnEnabled()) {
+            logger.warn(getTimeoutMessage());
+        }
+    }
+
+    private String getTimeoutMessage() {
+        return "Keep-alive response message was not received within " + 
+               getKeepAliveRequestTimeout() + " second(s).";
+    }
+
+    private void markStatus(IoSession session) {
+        int keepAliveTimeout = getKeepAliveRequestTimeout();
+        if (keepAliveTimeout > 0) {
+            // Mark only when keepAliveTimeout is enabled.
+            session.getConfig().setIdleTime(
+                    IdleStatus.READER_IDLE, getKeepAliveRequestTimeout());
+            session.setAttribute(WAITING_FOR_PONG);
+        }
+    }
+
+    private void resetStatus(IoSession session) {
+        session.getConfig().setIdleTime(
+                IdleStatus.READER_IDLE, getKeepAliveRequestInterval());
+        session.removeAttribute(WAITING_FOR_PONG);
+    }
+    
+    private boolean isKeepAliveMessage(IoSession session, Object message) {
+        return messageFactory.isRequest(session, message) ||
+               messageFactory.isResponse(session, message);
+    }
+}

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveFilter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveMessageFactory.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveMessageFactory.java?rev=594745&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveMessageFactory.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveMessageFactory.java Tue Nov 13 19:51:36 2007
@@ -0,0 +1,55 @@
+/*
+ *  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.apache.mina.filter.keepalive;
+
+import org.apache.mina.common.IoSession;
+
+/**
+ * Provides keep-alive messages to {@link KeepAliveFilter}.
+ *  
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public interface KeepAliveMessageFactory {
+    
+    /**
+     * Returns <tt>true</tt> if and only if the specified message is a
+     * keep-alive request message.
+     */
+    boolean isRequest(IoSession session, Object message);
+
+    /**
+     * Returns <tt>true</tt> if and only if the specified message is a 
+     * keep-alive response message;
+     */
+    boolean isResponse(IoSession session, Object message);
+    
+    /**
+     * Returns a (new) keep-alive request message.
+     * Returns <tt>null</tt> if no request is required.
+     */
+    Object getRequest(IoSession session);
+    
+    /**
+     * Returns a (new) response message for the specified keep-alive request.
+     * Returns <tt>null</tt> if no response is required.
+     */
+    Object getResponse(IoSession session, Object request);
+}

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveMessageFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveMessageFactory.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAlivePolicy.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAlivePolicy.java?rev=594745&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAlivePolicy.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAlivePolicy.java Tue Nov 13 19:51:36 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.apache.mina.filter.keepalive;
+
+
+
+/**
+ * Tells {@link KeepAliveFilter} what to do when a keep-alive response message
+ * was not received within a certain timeout.
+ * 
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public enum KeepAlivePolicy {
+    /**
+     * Do nothing; disables the filter.
+     */
+    OFF,
+    /**
+     * Log a warning message, but doesn't do anything else.
+     */
+    LOG,
+    /**
+     * Throw a {@link KeepAliveTimeoutException}.
+     */
+    EXCEPTION,
+    /**
+     * Close the connection.
+     */
+    CLOSE,
+}

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAlivePolicy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAlivePolicy.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveTimeoutException.java
URL: http://svn.apache.org/viewvc/mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveTimeoutException.java?rev=594745&view=auto
==============================================================================
--- mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveTimeoutException.java (added)
+++ mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveTimeoutException.java Tue Nov 13 19:51:36 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.apache.mina.filter.keepalive;
+
+/**
+ * A {@link RuntimeException} which is thrown when a keep-alive response
+ * message was not received within a certain timeout.
+ * 
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class KeepAliveTimeoutException extends RuntimeException {
+
+    private static final long serialVersionUID = -1985092764656546558L;
+
+    public KeepAliveTimeoutException() {
+        super();
+    }
+
+    public KeepAliveTimeoutException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public KeepAliveTimeoutException(String message) {
+        super(message);
+    }
+
+    public KeepAliveTimeoutException(Throwable cause) {
+        super(cause);
+    }
+}

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveTimeoutException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: mina/trunk/core/src/main/java/org/apache/mina/filter/keepalive/KeepAliveTimeoutException.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date