Package org.restlet.util

Source Code of org.restlet.util.SelectionRegistration

/**
* Copyright 2005-2011 Noelios Technologies.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL 1.0 (the
* "Licenses"). You can select the license that you prefer but you may not use
* this file except in compliance with one of these Licenses.
*
* You can obtain a copy of the LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1.php
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1.php
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/

package org.restlet.util;

import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

import org.restlet.Context;
import org.restlet.engine.io.IoUtils;

/**
* Represents a unique registration between a NIO selector and a selectable
* channel. For the operation codes, see the constants in {@link SelectionKey}.
*
* @author Jerome Louvel
* @see SelectionKey
*/
public class SelectionRegistration {

    /**
     * Returns the name of the given IO operation.
     *
     * @param operation
     *            The IO operation code.
     * @return The name of the given IO operation.
     */
    public static String getName(int operation) {
        StringBuilder result = new StringBuilder();

        if ((operation & SelectionKey.OP_ACCEPT) != 0) {
            result.append("ACCEPT ");
        }

        if ((operation & SelectionKey.OP_CONNECT) != 0) {
            result.append("CONNECT ");
        }

        if ((operation & SelectionKey.OP_READ) != 0) {
            result.append("READ ");
        }

        if ((operation & SelectionKey.OP_WRITE) != 0) {
            result.append("WRITE ");
        }

        if (operation == 0) {
            result.append("NONE ");
        }

        if (result.length() == 0) {
            result.append(operation);
        }

        return result.toString();
    }

    /** The barrier used for blocking/unblocking support. */
    private final CyclicBarrier barrier;

    /** Indicates if that registration has been canceled. */
    private volatile boolean canceling;

    /** The IO operations interest. */
    private volatile int interestOperations;

    /** The selection listener that will be notified. */
    private volatile SelectionListener listener;

    /** The previous IO operations interest. */
    private volatile int previousInterest;

    /** The IO operations ready. */
    private volatile int readyOperations;

    /** The parent selectable channel. */
    private final SelectableChannel selectableChannel;

    /** The active selection key. */
    private volatile SelectionKey selectionKey;

    /**
     * Constructor.
     *
     * @param interestOperations
     *            The IO operations interest.
     * @param listener
     *            The selection listener that will be notified.
     */
    public SelectionRegistration(int interestOperations,
            SelectionListener listener) {
        this(null, interestOperations, listener);
    }

    /**
     * Constructor.
     *
     * @param selectableChannel
     *            The parent selectable channel.
     * @param interestOperations
     *            The IO operations interest.
     * @param listener
     *            The selection listener that will be notified.
     */
    public SelectionRegistration(SelectableChannel selectableChannel,
            int interestOperations, SelectionListener listener) {
        this.canceling = false;
        this.selectableChannel = selectableChannel;
        this.barrier = new CyclicBarrier(2);
        this.listener = listener;
        this.setInterestOperations(interestOperations);
    }

    /**
     * Adds a given operations to the current list.
     *
     * @param interest
     */
    public void addInterestOperations(int interest) {
        setInterestOperations(getInterestOperations() & interest);
    }

    /**
     * Blocks the calling thread.
     *
     * @see #block()
     */
    public void block() {
        try {
            if (Context.getCurrentLogger().isLoggable(Level.FINEST)) {
                Context.getCurrentLogger().log(
                        Level.FINEST,
                        "Calling thread about to block on the NIO selection registration. Timeout: "
                                + TimeUnit.MILLISECONDS
                                        .toMillis(IoUtils.TIMEOUT_MS)
                                + " ms. Waiting: "
                                + this.barrier.getNumberWaiting());
            }

            this.barrier.await(IoUtils.TIMEOUT_MS, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            Context.getCurrentLogger().log(Level.WARNING,
                    "Unable to block the thread at the cyclic barrier", e);
        }
    }

    /**
     * Clears the registration.
     */
    public void clear() {
        this.barrier.reset();
        this.canceling = false;
        this.interestOperations = 0;
        this.previousInterest = 0;
        this.readyOperations = 0;
        this.selectionKey = null;
    }

    /**
     * Returns the IO operations interest.
     *
     * @return The IO operations interest.
     */
    public int getInterestOperations() {
        return interestOperations;
    }

    /**
     * Returns the selection listener that will be notified.
     *
     * @return The selection listener that will be notified.
     */
    public SelectionListener getListener() {
        return listener;
    }

    /**
     * Returns the IO operations ready.
     *
     * @return The IO operations ready.
     */
    public int getReadyOperations() {
        return readyOperations;
    }

    /**
     * Returns the parent selectable channel.
     *
     * @return The parent selectable channel.
     */
    public SelectableChannel getSelectableChannel() {
        return this.selectableChannel;
    }

    /**
     * Indicates if a canceling of the registration has been requested.
     *
     * @return True if the registration has been canceled.
     */
    public boolean isCanceling() {
        return this.canceling;
    }

    /**
     * Indicates if the NIO channel is connectable.
     *
     * @return True if the NIO channel is connectable.
     */
    public boolean isConnectable() {
        return (getReadyOperations() & SelectionKey.OP_CONNECT) != 0;
    }

    /**
     * Indicates if the operations of interest are ready.
     *
     * @return True if the operations of interest are ready.
     */
    public boolean isInterestReady() {
        return ((getReadyOperations() & getInterestOperations()) > 0);
    }

    /**
     * Indicates if the NIO channel is readable.
     *
     * @return True if the NIO channel is readable.
     */
    public boolean isReadable() {
        return (getReadyOperations() & SelectionKey.OP_READ) != 0;
    }

    /**
     * Indicates if the NIO channel is writable.
     *
     * @return True if the NIO channel is writable.
     */
    public boolean isWritable() {
        return (getReadyOperations() & SelectionKey.OP_WRITE) != 0;
    }

    /**
     * Called back with some interest operations are ready. By default, it calls
     * back the registered listener provided by {@link #getListener()}.
     *
     * @param readyOperations
     *            The ready operations.
     */
    public void onSelected(int readyOperations) {
        this.readyOperations = readyOperations;

        if ((getListener() != null) && isInterestReady()) {
            getListener().onSelected();
        }
    }

    /**
     * Effectively registers the {@link #getSelectableChannel()} with the given
     * {@link Selector} for the {@link #getInterestOperations()} operations.
     *
     * @param selector
     *            The NIO selector to register to.
     */
    public SelectionKey register(Selector selector) {
        try {
            this.selectionKey = getSelectableChannel().register(selector,
                    getInterestOperations(), this);
        } catch (ClosedChannelException cce) {
            Context.getCurrentLogger().log(Level.FINE,
                    "Unable to register again", cce);
        }

        return this.selectionKey;
    }

    /**
     * Resume interest in new listener notifications. This should be called
     * after a {@link #suspend()} call.
     */
    public void resume() {
        if (Context.getCurrentLogger().isLoggable(Level.FINER)) {
            Context.getCurrentLogger().log(Level.FINER,
                    "Resuming previous NIO interest");
        }

        setInterestOperations(this.previousInterest);
    }

    /**
     * Sets interest in canceling the registration.
     *
     * @param canceling
     *            True if a canceling request is made.
     */
    public void setCanceling(boolean canceling) {
        this.canceling = canceling;
    }

    /**
     * Sets the IO operations interest. Note that it also clears the ready
     * operations.
     *
     * @param interest
     *            The IO operations interest.
     * @return True if the operations effectively changed.
     */
    public boolean setInterestOperations(int interest) {
        boolean result = false;

        if (this.interestOperations != interest) {
            this.interestOperations = interest;
            result = true;
        }

        setReadyOperations(0);
        return result;
    }

    /**
     * Sets the selection listener that will be notified.
     *
     * @param listener
     *            The selection listener that will be notified.
     */
    public void setListener(SelectionListener listener) {
        this.listener = listener;
    }

    /**
     * Sets interest in no IO operations.
     */
    public void setNoInterest() {
        setInterestOperations(0);
    }

    /**
     * Sets interest in IO read operations.
     */
    public void setReadInterest() {
        setInterestOperations(SelectionKey.OP_READ);
    }

    /**
     * Sets the IO operations ready.
     *
     * @param readyOperations
     *            The IO operations ready.
     */
    public void setReadyOperations(int readyOperations) {
        this.readyOperations = readyOperations;
    }

    /**
     * Sets interest in IO read operations.
     */
    public void setWriteInterest() {
        setInterestOperations(SelectionKey.OP_WRITE);
    }

    /**
     * Suspend interest in new listener notifications. By default, remembers the
     * current interest and calls {@link #setInterestOperations(int)} with a 0
     * value.
     */
    public void suspend() {
        this.previousInterest = getInterestOperations();
        setInterestOperations(0);
    }

    @Override
    public String toString() {
        return "Interest= " + getName(getInterestOperations()) + ", Ready="
                + getName(getReadyOperations()) + ", Canceling="
                + Boolean.toString(isCanceling());
    }

    /**
     * Unblocks the optionally blocked thread.
     *
     * @see #block()
     */
    public void unblock() {
        if (Context.getCurrentLogger().isLoggable(Level.FINEST)) {
            Context.getCurrentLogger().log(
                    Level.FINEST,
                    "Calling thread about to unblock the NIO selection registration. Timeout: "
                            + TimeUnit.MILLISECONDS
                                    .toMillis(IoUtils.TIMEOUT_MS)
                            + " ms. Waiting: "
                            + this.barrier.getNumberWaiting());
        }

        try {
            this.barrier.await(IoUtils.TIMEOUT_MS, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            Context.getCurrentLogger()
                    .log(Level.WARNING,
                            "Unable to unblock the waiting thread at the cyclic barrier",
                            e);
        }
    }

    /**
     * Effectively updates the registration of the
     * {@link #getSelectableChannel()} with the given {@link Selector} for the
     * {@link #getInterestOperations()} operations.
     *
     * @return The updated selection key or a new one if it was registered
     *         again.
     */
    public SelectionKey update() {
        if (this.selectionKey.isValid()) {
            if (isCanceling()) {
                Context.getCurrentLogger().log(Level.FINER,
                        "Cancelling of the selection key requested");
                this.selectionKey.cancel();
            } else {
                try {
                    if (Context.getCurrentLogger().isLoggable(Level.FINEST)) {
                        Context.getCurrentLogger()
                                .log(Level.FINEST,
                                        "Update key (old | new) : "
                                                + SelectionRegistration
                                                        .getName(this.selectionKey
                                                                .interestOps())
                                                + " | "
                                                + SelectionRegistration
                                                        .getName(getInterestOperations()));
                    }

                    this.selectionKey.interestOps(getInterestOperations());
                } catch (CancelledKeyException cke) {
                    Context.getCurrentLogger()
                            .log(Level.FINE,
                                    "Unable to update a cancelled key, registering again",
                                    cke);
                    this.selectionKey = register(this.selectionKey.selector());
                }
            }
        } else {
            Context.getCurrentLogger().log(Level.FINE,
                    "Invalid key detected, registering again");
            this.selectionKey = register(this.selectionKey.selector());
        }

        return this.selectionKey;
    }

}
TOP

Related Classes of org.restlet.util.SelectionRegistration

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.