/*
* 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.ignite.internal.util.nio;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.util.typedef.internal.LT;
import org.apache.ignite.lang.IgniteInClosure;
/**
* Filter chain implementation for nio server filters.
*/
public class GridNioFilterChain<T> extends GridNioFilterAdapter {
/** Grid logger. */
private IgniteLogger log;
/** Listener that will be used to notify on server events. */
private GridNioServerListener<T> lsnr;
/** Head of filter list. */
private GridNioFilter head;
/** Tail of filter list. */
private GridNioFilter tail;
/** Cached value for toString method. */
private volatile String str;
/**
* Constructor.
*
* @param log Logger instance.
* @param lsnr Listener for events passed through chain.
* @param head First filter in chain, it expected to be connected to actual endpoint.
* @param filters Filters applied between listener and head. Will be inserted in the same order,
* so chain will look like (lsnr) -> (filters[0]) -> ... -> (filters[n]) -> (head).
*/
public GridNioFilterChain(IgniteLogger log, GridNioServerListener<T> lsnr, GridNioFilter head,
GridNioFilter... filters) {
super("FilterChain");
this.log = log;
this.lsnr = lsnr;
this.head = head;
GridNioFilter prev;
tail = prev = new TailFilter();
for (GridNioFilter filter : filters) {
prev.nextFilter(filter);
filter.previousFilter(prev);
prev = filter;
}
prev.nextFilter(head);
head.previousFilter(prev);
}
/** {@inheritDoc} */
public String toString() {
if (str == null) {
StringBuilder res = new StringBuilder("FilterChain[filters=[");
GridNioFilter ref = tail.nextFilter();
while (ref != head) {
res.append(ref);
ref = ref.nextFilter();
if (ref != head)
res.append(", ");
}
res.append(']');
// It is OK if this variable will be rewritten concurrently.
str = res.toString();
}
return str;
}
/**
* Starts all filters in order from application layer to the network layer.
*/
@Override public void start() {
GridNioFilter ref = tail.nextFilter();
// Walk through the linked list and start all the filters.
while (ref != head) {
ref.start();
ref = ref.nextFilter();
}
}
/**
* Stops all filters in order from network layer to the application layer.
*/
@Override public void stop() {
GridNioFilter ref = head.previousFilter();
// Walk through the linked list and stop all the filters.
while (ref != tail) {
ref.stop();
ref = ref.previousFilter();
}
}
/**
* Starts chain notification from head to tail.
*
* @param ses Session that was created.
* @throws IgniteCheckedException If IgniteCheckedException occurred while handling event.
*/
@Override public void onSessionOpened(GridNioSession ses) throws IgniteCheckedException {
head.onSessionOpened(ses);
}
/**
* Starts chain notification from head to tail.
*
* @param ses Session that was closed.
* @throws IgniteCheckedException If IgniteCheckedException occurred while handling event.
*/
@Override public void onSessionClosed(GridNioSession ses) throws IgniteCheckedException {
head.onSessionClosed(ses);
}
/**
* Starts chain notification from head to tail.
*
* @param ses Session in which GridNioException was caught.
* @param e IgniteCheckedException instance.
*/
@Override public void onExceptionCaught(GridNioSession ses, IgniteCheckedException e) {
try {
head.onExceptionCaught(ses, e);
}
catch (Exception ex) {
LT.error(log, ex, "Failed to forward GridNioException to filter chain [ses=" + ses + ", e=" + e + ']');
}
}
/**
* Starts chain notification from head to tail.
*
* @param ses Session in which message was received.
* @param msg Received message.
* @throws IgniteCheckedException If IgniteCheckedException occurred while handling event.
*/
@Override public void onMessageReceived(GridNioSession ses, Object msg) throws IgniteCheckedException {
head.onMessageReceived(ses, msg);
}
/**
* Starts chain notification from tail to head.
*
* @param ses Session to which message should be written.
* @param msg Message to write.
* @return Send future.
* @throws IgniteCheckedException If IgniteCheckedException occurred while handling event.
*/
@Override public GridNioFuture<?> onSessionWrite(
GridNioSession ses,
Object msg,
boolean fut,
IgniteInClosure<IgniteException> ackC
) throws IgniteCheckedException {
return tail.onSessionWrite(ses, msg, fut, ackC);
}
/**
* Starts chain notification from tail to head.
*
* @param ses Session to close.
* @return Close future.
* @throws IgniteCheckedException If IgniteCheckedException occurred while handling event.
*/
@Override public GridNioFuture<Boolean> onSessionClose(GridNioSession ses) throws IgniteCheckedException {
return tail.onSessionClose(ses);
}
/** {@inheritDoc} */
@Override public void onSessionIdleTimeout(GridNioSession ses) throws IgniteCheckedException {
head.onSessionIdleTimeout(ses);
}
/** {@inheritDoc} */
@Override public void onSessionWriteTimeout(GridNioSession ses) throws IgniteCheckedException {
head.onSessionWriteTimeout(ses);
}
/**
* Starts chain notification from tail to head.
*
* @param ses Session to pause reads.
* @return Future.
* @throws IgniteCheckedException If IgniteCheckedException occurred while handling event.
*/
@Override public GridNioFuture<?> onPauseReads(GridNioSession ses) throws IgniteCheckedException {
return tail.onPauseReads(ses);
}
/**
* Starts chain notification from tail to head.
*
* @param ses Session to resume reads.
* @return Future.
* @throws IgniteCheckedException If IgniteCheckedException occurred while handling event.
*/
@Override public GridNioFuture<?> onResumeReads(GridNioSession ses) throws IgniteCheckedException {
return tail.onResumeReads(ses);
}
/**
* Tail filter that handles all incoming events.
*/
private class TailFilter extends GridNioFilterAdapter {
/**
* Constructs tail filter.
*/
private TailFilter() {
super("TailFilter");
}
/** {@inheritDoc} */
@Override public void onSessionOpened(GridNioSession ses) {
lsnr.onConnected(ses);
}
/** {@inheritDoc} */
@Override public void onSessionClosed(GridNioSession ses) {
lsnr.onDisconnected(ses, null);
}
/** {@inheritDoc} */
@Override public void onExceptionCaught(GridNioSession ses, IgniteCheckedException ex) {
lsnr.onDisconnected(ses, ex);
}
/** {@inheritDoc} */
@Override public GridNioFuture<?> onSessionWrite(GridNioSession ses,
Object msg,
boolean fut,
IgniteInClosure<IgniteException> ackC) throws IgniteCheckedException {
return proceedSessionWrite(ses, msg, fut, ackC);
}
/** {@inheritDoc} */
@Override public GridNioFuture<Boolean> onSessionClose(GridNioSession ses) throws IgniteCheckedException {
return proceedSessionClose(ses);
}
/** {@inheritDoc} */
@Override public void onMessageReceived(GridNioSession ses, Object msg) {
lsnr.onMessage(ses, (T)msg);
}
/** {@inheritDoc} */
@Override public void onSessionIdleTimeout(GridNioSession ses) throws IgniteCheckedException {
lsnr.onSessionIdleTimeout(ses);
}
/** {@inheritDoc} */
@Override public void onSessionWriteTimeout(GridNioSession ses) throws IgniteCheckedException {
lsnr.onSessionWriteTimeout(ses);
}
/** {@inheritDoc} */
@Override public GridNioFuture<?> onPauseReads(GridNioSession ses) throws IgniteCheckedException {
return proceedPauseReads(ses);
}
/** {@inheritDoc} */
@Override public GridNioFuture<?> onResumeReads(GridNioSession ses) throws IgniteCheckedException {
return proceedResumeReads(ses);
}
}
}