All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.mina.filter.keepalive.KeepAliveFilter Maven / Gradle / Ivy

/**
 * Copyright 2007-2015, Kaazing Corporation. All rights reserved.
 *
 * Licensed 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.core.filterchain.IoFilter;
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.filterchain.IoFilterChain;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoEventType;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.session.IoSessionConfig;
import org.apache.mina.core.write.DefaultWriteRequest;
import org.apache.mina.core.write.WriteRequest;

/**
 * An {@link IoFilter} that sends a keep-alive request on
 * {@link IoEventType#SESSION_IDLE} and sends back the response for the
 * sent keep-alive request.
 *
 * 

Interference with {@link IoSessionConfig#setIdleTime(IdleStatus, int)}

* * This filter adjusts idleTime of the {@link IdleStatus}s that * this filter is interested in automatically (e.g. {@link IdleStatus#READER_IDLE} * and {@link IdleStatus#WRITER_IDLE}.) Changing the idleTime * of the {@link IdleStatus}s can lead this filter to a unexpected behavior. * Please also note that any {@link IoFilter} and {@link IoHandler} behind * {@link KeepAliveFilter} will not get any {@link IoEventType#SESSION_IDLE} * event. To receive the internal {@link IoEventType#SESSION_IDLE} event, * you can call {@link #setForwardEvent(boolean)} with true. * *

Implementing {@link KeepAliveMessageFactory}

* * 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: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
NameDescriptionImplementation
ActiveYou 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 keepAliveRequestTimeout seconds. Otherwise, * the specified {@link KeepAliveRequestTimeoutHandler} will be invoked. * If a keep-alive request is received, its response also should be sent back. * Both {@link KeepAliveMessageFactory#getRequest(IoSession)} and * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must * return a non-null.
Semi-activeYou 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. * Both {@link KeepAliveMessageFactory#getRequest(IoSession)} and * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must * return a non-null, and the timeoutHandler property * should be set to {@link KeepAliveRequestTimeoutHandler#NOOP}, * {@link KeepAliveRequestTimeoutHandler#LOG} or the custom {@link KeepAliveRequestTimeoutHandler} * implementation that doesn't affect the session state nor throw an exception. *
PassiveYou 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.{@link KeepAliveMessageFactory#getRequest(IoSession)} must return * null and {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} * must return a non-null.
Deaf SpeakerYou want a keep-alive request to be sent when the reader is idle, but * you don't want to send any response back.{@link KeepAliveMessageFactory#getRequest(IoSession)} must return * a non-null, * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must * return null and the timeoutHandler must be set to * {@link KeepAliveRequestTimeoutHandler#DEAF_SPEAKER}.
Silent ListenerYou don't want to send a keep-alive request by yourself nor send any * response back.Both {@link KeepAliveMessageFactory#getRequest(IoSession)} and * {@link KeepAliveMessageFactory#getResponse(IoSession, Object)} must * return null.
* Please note that you must implement * {@link KeepAliveMessageFactory#isRequest(IoSession, Object)} and * {@link KeepAliveMessageFactory#isResponse(IoSession, Object)} properly * whatever case you chose. * *

Handling timeout

* * {@link KeepAliveFilter} will notify its {@link KeepAliveRequestTimeoutHandler} * when {@link KeepAliveFilter} didn't receive the response message for a sent * keep-alive message. The default handler is {@link KeepAliveRequestTimeoutHandler#CLOSE}, * but you can use other presets such as {@link KeepAliveRequestTimeoutHandler#NOOP}, * {@link KeepAliveRequestTimeoutHandler#LOG} or {@link KeepAliveRequestTimeoutHandler#EXCEPTION}. * You can even implement your own handler. * *

Special handler: {@link KeepAliveRequestTimeoutHandler#DEAF_SPEAKER}

* * {@link KeepAliveRequestTimeoutHandler#DEAF_SPEAKER} is a special handler which is * dedicated for the 'deaf speaker' mode mentioned above. Setting the * timeoutHandler property to {@link KeepAliveRequestTimeoutHandler#DEAF_SPEAKER} * stops this filter from waiting for response messages and therefore disables * response timeout detection. * * @author Apache MINA Project * @org.apache.xbean.XBean */ public class KeepAliveFilter extends IoFilterAdapter { private final AttributeKey WAITING_FOR_RESPONSE = new AttributeKey( getClass(), "waitingForResponse"); private final AttributeKey IGNORE_READER_IDLE_ONCE = new AttributeKey( getClass(), "ignoreReaderIdleOnce"); private final KeepAliveMessageFactory messageFactory; private final IdleStatus interestedIdleStatus; private volatile KeepAliveRequestTimeoutHandler requestTimeoutHandler; private volatile int requestInterval; private volatile int requestTimeout; private volatile boolean forwardEvent; /** * Creates a new instance with the default properties. * The default property values are: *
    *
  • interestedIdleStatus - {@link IdleStatus#READER_IDLE}
  • *
  • policy = {@link KeepAliveRequestTimeoutHandler#CLOSE}
  • *
  • keepAliveRequestInterval - 60 (seconds)
  • *
  • keepAliveRequestTimeout - 30 (seconds)
  • *
*/ public KeepAliveFilter(KeepAliveMessageFactory messageFactory) { this(messageFactory, IdleStatus.READER_IDLE, KeepAliveRequestTimeoutHandler.CLOSE); } /** * Creates a new instance with the default properties. * The default property values are: *
    *
  • policy = {@link KeepAliveRequestTimeoutHandler#CLOSE}
  • *
  • keepAliveRequestInterval - 60 (seconds)
  • *
  • keepAliveRequestTimeout - 30 (seconds)
  • *
*/ public KeepAliveFilter( KeepAliveMessageFactory messageFactory, IdleStatus interestedIdleStatus) { this(messageFactory, interestedIdleStatus, KeepAliveRequestTimeoutHandler.CLOSE, 60, 30); } /** * Creates a new instance with the default properties. * The default property values are: *
    *
  • interestedIdleStatus - {@link IdleStatus#READER_IDLE}
  • *
  • keepAliveRequestInterval - 60 (seconds)
  • *
  • keepAliveRequestTimeout - 30 (seconds)
  • *
*/ public KeepAliveFilter( KeepAliveMessageFactory messageFactory, KeepAliveRequestTimeoutHandler policy) { this(messageFactory, IdleStatus.READER_IDLE, policy, 60, 30); } /** * Creates a new instance with the default properties. * The default property values are: *
    *
  • keepAliveRequestInterval - 60 (seconds)
  • *
  • keepAliveRequestTimeout - 30 (seconds)
  • *
*/ public KeepAliveFilter( KeepAliveMessageFactory messageFactory, IdleStatus interestedIdleStatus, KeepAliveRequestTimeoutHandler policy) { this(messageFactory, interestedIdleStatus, policy, 60, 30); } /** * Creates a new instance. */ public KeepAliveFilter( KeepAliveMessageFactory messageFactory, IdleStatus interestedIdleStatus, KeepAliveRequestTimeoutHandler policy, int keepAliveRequestInterval, int keepAliveRequestTimeout) { if (messageFactory == null) { throw new NullPointerException("messageFactory"); } if (interestedIdleStatus == null) { throw new NullPointerException("interestedIdleStatus"); } if (policy == null) { throw new NullPointerException("policy"); } this.messageFactory = messageFactory; this.interestedIdleStatus = interestedIdleStatus; requestTimeoutHandler = policy; setRequestInterval(keepAliveRequestInterval); setRequestTimeout(keepAliveRequestTimeout); } public IdleStatus getInterestedIdleStatus() { return interestedIdleStatus; } public KeepAliveRequestTimeoutHandler getRequestTimeoutHandler() { return requestTimeoutHandler; } public void setRequestTimeoutHandler(KeepAliveRequestTimeoutHandler timeoutHandler) { if (timeoutHandler == null) { throw new NullPointerException("timeoutHandler"); } requestTimeoutHandler = timeoutHandler; } public int getRequestInterval() { return requestInterval; } public void setRequestInterval(int keepAliveRequestInterval) { if (keepAliveRequestInterval <= 0) { throw new IllegalArgumentException( "keepAliveRequestInterval must be a positive integer: " + keepAliveRequestInterval); } requestInterval = keepAliveRequestInterval; } public int getRequestTimeout() { return requestTimeout; } public void setRequestTimeout(int keepAliveRequestTimeout) { if (keepAliveRequestTimeout <= 0) { throw new IllegalArgumentException( "keepAliveRequestTimeout must be a positive integer: " + keepAliveRequestTimeout); } requestTimeout = keepAliveRequestTimeout; } public KeepAliveMessageFactory getMessageFactory() { return messageFactory; } /** * Returns true if and only if this filter forwards * a {@link IoEventType#SESSION_IDLE} event to the next filter. * By default, the value of this property is false. */ public boolean isForwardEvent() { return forwardEvent; } /** * Sets if this filter needs to forward a * {@link IoEventType#SESSION_IDLE} event to the next filter. * By default, the value of this property is false. */ public void setForwardEvent(boolean forwardEvent) { this.forwardEvent = forwardEvent; } @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 { if (status == interestedIdleStatus) { if (!session.containsAttribute(WAITING_FOR_RESPONSE)) { Object pingMessage = messageFactory.getRequest(session); if (pingMessage != null) { nextFilter.filterWrite( session, new DefaultWriteRequest(pingMessage)); // If policy is OFF, there's no need to wait for // the response. if (getRequestTimeoutHandler() != KeepAliveRequestTimeoutHandler.DEAF_SPEAKER) { markStatus(session); if (interestedIdleStatus == IdleStatus.BOTH_IDLE) { session.setAttribute(IGNORE_READER_IDLE_ONCE); } } else { resetStatus(session); } } } else { handlePingTimeout(session); } } else if (status == IdleStatus.READER_IDLE) { if (session.removeAttribute(IGNORE_READER_IDLE_ONCE) == null) { if (session.containsAttribute(WAITING_FOR_RESPONSE)) { handlePingTimeout(session); } } } if (forwardEvent) { nextFilter.sessionIdle(session, status); } } private void handlePingTimeout(IoSession session) throws Exception { resetStatus(session); KeepAliveRequestTimeoutHandler handler = getRequestTimeoutHandler(); if (handler == KeepAliveRequestTimeoutHandler.DEAF_SPEAKER) { return; } handler.keepAliveRequestTimedOut(this, session); } private void markStatus(IoSession session) { session.getConfig().setIdleTime(interestedIdleStatus, 0); session.getConfig().setReaderIdleTime(getRequestTimeout()); session.setAttribute(WAITING_FOR_RESPONSE); } private void resetStatus(IoSession session) { session.getConfig().setReaderIdleTime(0); session.getConfig().setWriterIdleTime(0); session.getConfig().setIdleTime( interestedIdleStatus, getRequestInterval()); session.removeAttribute(WAITING_FOR_RESPONSE); } private boolean isKeepAliveMessage(IoSession session, Object message) { return messageFactory.isRequest(session, message) || messageFactory.isResponse(session, message); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy