Package org.glassfish.grizzly.utils

Source Code of org.glassfish.grizzly.utils.IdleTimeoutFilter$DefaultWorker

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package org.glassfish.grizzly.utils;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.filterchain.BaseFilter;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;

/**
* The Filter is responsible for tracking {@link Connection} activity and closing
* {@link Connection} ones it becomes idle for certain amount of time.
* Unlike {@link ActivityCheckFilter}, this Filter assumes {@link Connection}
* is idle, when no event is being executed on it. But if some event processing
* was suspended - this Filter still assumes {@link Connection} is active.
*
* @see ActivityCheckFilter
*
* @author Alexey Stashok
*/
public class IdleTimeoutFilter extends BaseFilter {

    public static final Long FOREVER = Long.MAX_VALUE;
    public static final Long FOREVER_SPECIAL = FOREVER - 1;
   
    public static final String IDLE_ATTRIBUTE_NAME = "connection-idle-attribute";
    private static final Attribute<IdleRecord> IDLE_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(
            IDLE_ATTRIBUTE_NAME, new NullaryFunction<IdleRecord>() {

        @Override
        public IdleRecord evaluate() {
            return new IdleRecord();
        }
    });
   
    private final TimeoutResolver timeoutResolver;
    private final DelayedExecutor.DelayQueue<Connection> queue;
    private final DelayedExecutor.Resolver<Connection> resolver;

    private final FilterChainContext.CompletionListener contextCompletionListener =
            new ContextCompletionListener();


    // ------------------------------------------------------------ Constructors


    public IdleTimeoutFilter(final DelayedExecutor executor,
                             final long timeout,
                             final TimeUnit timeoutUnit) {

        this(executor, timeout, timeoutUnit, null);

    }


    @SuppressWarnings("UnusedDeclaration")
    public IdleTimeoutFilter(final DelayedExecutor executor,
                             final TimeoutResolver timeoutResolver) {
        this(executor, timeoutResolver, null);
    }


    public IdleTimeoutFilter(final DelayedExecutor executor,
                             final long timeout,
                             final TimeUnit timeUnit,
                             final TimeoutHandler handler) {

        this(executor,
                new DefaultWorker(handler),
                new IdleTimeoutResolver(convertToMillis(timeout, timeUnit)));
    }


    public IdleTimeoutFilter(final DelayedExecutor executor,
                             final TimeoutResolver timeoutResolver,
                             final TimeoutHandler handler) {

        this(executor, new DefaultWorker(handler), timeoutResolver);
    }


    protected IdleTimeoutFilter(final DelayedExecutor executor,
                                final DelayedExecutor.Worker<Connection> worker,
                                final TimeoutResolver timeoutResolver) {

        if (executor == null) {
            throw new IllegalArgumentException("executor cannot be null");
        }

        this.timeoutResolver = timeoutResolver;
        resolver = new Resolver();
        queue = executor.createDelayQueue(worker, resolver);

    }


    // ----------------------------------------------------- Methods from Filter



    @Override
    public NextAction handleAccept(final FilterChainContext ctx) throws IOException {
        queue.add(ctx.getConnection(), FOREVER, TimeUnit.MILLISECONDS);

        queueAction(ctx);
        return ctx.getInvokeAction();
    }

    @Override
    public NextAction handleConnect(final FilterChainContext ctx) throws IOException {
        queue.add(ctx.getConnection(), FOREVER, TimeUnit.MILLISECONDS);

        queueAction(ctx);
        return ctx.getInvokeAction();
    }
   
    @Override
    public NextAction handleRead(final FilterChainContext ctx) throws IOException {
        queueAction(ctx);
        return ctx.getInvokeAction();
    }

    @Override
    public NextAction handleWrite(final FilterChainContext ctx) throws IOException {
        queueAction(ctx);
        return ctx.getInvokeAction();
    }

    @Override
    public NextAction handleClose(final FilterChainContext ctx) throws IOException {
        queue.remove(ctx.getConnection());
        return ctx.getInvokeAction();
    }


    // ---------------------------------------------------------- Public Methods


    @SuppressWarnings("UnusedDeclaration")
    public DelayedExecutor.Resolver<Connection> getResolver() {
        return resolver;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public static DelayedExecutor createDefaultIdleDelayedExecutor() {

        return createDefaultIdleDelayedExecutor(1000, TimeUnit.MILLISECONDS);

    }

    @SuppressWarnings({"UnusedDeclaration"})
    public static DelayedExecutor createDefaultIdleDelayedExecutor(final long checkInterval,
                                                                   final TimeUnit checkIntervalUnit) {

        final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {

            @Override
            public Thread newThread(Runnable r) {
                final Thread newThread = new Thread(r);
                newThread.setName("Grizzly-IdleTimeoutFilter-IdleCheck");
                newThread.setDaemon(true);
                return newThread;
            }
        });
        return new DelayedExecutor(executor,
                                   ((checkInterval > 0)
                                       ? checkInterval
                                       : 1000L),
                                   ((checkIntervalUnit != null)
                                       ? checkIntervalUnit
                                       : TimeUnit.MILLISECONDS));

    }


    /**
     * Provides an override mechanism for the default timeout. 
     *
     * @param connection The {@link Connection} which is having the idle detection
     *          adjusted.
     * @param timeout the new idle timeout.
     * @param timeunit {@link TimeUnit}.
     */
    public static void setCustomTimeout(final Connection connection,
                                        final long timeout,
                                        final TimeUnit timeunit) {
        IDLE_ATTR.get(connection).setInitialTimeoutMillis(
                convertToMillis(timeout, timeunit));
    }

    // ------------------------------------------------------- Protected Methods


    protected void queueAction(final FilterChainContext ctx) {
        final Connection connection = ctx.getConnection();
        final IdleRecord idleRecord = IDLE_ATTR.get(connection);
        if (idleRecord.counter.getAndIncrement() == 0) {
            idleRecord.timeoutMillis.set(FOREVER);
        }

        ctx.addCompletionListener(contextCompletionListener);
    }

    // ------------------------------------------------------- Private Methods
   
    private static long convertToMillis(final long time, final TimeUnit timeUnit) {
        return time >= 0 ? TimeUnit.MILLISECONDS.convert(time, timeUnit) : FOREVER;
    }

    // ----------------------------------------------------------- Inner Classes


    public interface TimeoutHandler {

        void onTimeout(final Connection c);

    }

    public interface TimeoutResolver {

        long getTimeout(FilterChainContext ctx);

    }


    private final class ContextCompletionListener
            implements FilterChainContext.CompletionListener {

        @Override
        public void onComplete(final FilterChainContext ctx) {
            final Connection connection = ctx.getConnection();
            final IdleRecord idleRecord = IDLE_ATTR.get(connection);
            // Small trick to not synchronize this block and queueAction();
            idleRecord.timeoutMillis.set(FOREVER_SPECIAL);
            if (idleRecord.counter.decrementAndGet() == 0) {
                final long timeout = timeoutResolver.getTimeout(ctx);
                if (timeout == FOREVER) {
                    idleRecord.timeoutMillis.compareAndSet(FOREVER_SPECIAL, FOREVER);
                } else {
                    idleRecord.timeoutMillis.compareAndSet(FOREVER_SPECIAL,
                        System.currentTimeMillis() + timeout);
                }
            }
        }
    } // END ContextCompletionListener


    // ---------------------------------------------------------- Nested Classes

    private static final class IdleTimeoutResolver implements TimeoutResolver {

        private final long defaultTimeoutMillis;
        // -------------------------------------------------------- Constructors


        IdleTimeoutResolver(final long defaultTimeoutMillis) {
            this.defaultTimeoutMillis = defaultTimeoutMillis;
        }

        // ---------------------------------------- Methods from TimeoutResolver


        @Override
        public long getTimeout(final FilterChainContext ctx) {
            return IDLE_ATTR.get(ctx.getConnection()).getInitialTimeoutMillis(defaultTimeoutMillis);
        }
    }


    private static final class Resolver implements DelayedExecutor.Resolver<Connection> {

        @Override
        public boolean removeTimeout(final Connection connection) {
            IDLE_ATTR.get(connection).timeoutMillis.set(0);
            return true;
        }

        @Override
        public long getTimeoutMillis(final Connection connection) {
            return IDLE_ATTR.get(connection).timeoutMillis.get();
        }

        @Override
        public void setTimeoutMillis(final Connection connection,
                final long timeoutMillis) {
            IDLE_ATTR.get(connection).timeoutMillis.set(timeoutMillis);
        }

    } // END Resolver

    private static final class IdleRecord {

        private volatile boolean isInitialSet = false;
        private long initialTimeoutMillis;
        private final AtomicLong timeoutMillis;
        private final AtomicInteger counter;

        private IdleRecord() {
            counter = new AtomicInteger();
            timeoutMillis = new AtomicLong();
        }

        private long getInitialTimeoutMillis(final long defaultTimeoutMillis) {
            return isInitialSet ? initialTimeoutMillis : defaultTimeoutMillis;
        }
       
        private void setInitialTimeoutMillis(final long initialTimeoutMillis) {
            this.initialTimeoutMillis = initialTimeoutMillis;
            isInitialSet = true;
        }

    } // END IdleRecord

    private static final class DefaultWorker implements DelayedExecutor.Worker<Connection> {

        private final TimeoutHandler handler;


        // -------------------------------------------------------- Constructors


        DefaultWorker(final TimeoutHandler handler) {

            this.handler = handler;

        }


        // --------------------------------- Methods from DelayedExecutor.Worker

        @Override
        public boolean doWork(final Connection connection) {
            if (connection.isOpen()) {
                if (handler != null) {
                    handler.onTimeout(connection);
                }
                connection.closeSilently();
            }

            return true;
        }

    } // END DefaultWorker


}
TOP

Related Classes of org.glassfish.grizzly.utils.IdleTimeoutFilter$DefaultWorker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.