/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*/
package com.sun.sgs.impl.nio;
import static java.nio.channels.SelectionKey.OP_ACCEPT;
import static java.nio.channels.SelectionKey.OP_CONNECT;
import static java.nio.channels.SelectionKey.OP_READ;
import static java.nio.channels.SelectionKey.OP_WRITE;
import java.nio.channels.SelectionKey;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* Utility methods for the asynchronous IO implementation.
*/
final class Util {
/** Prevents instantiation of this class. */
private Util() { }
/**
* Returns the given exception with its cause initialized. The
* original exception is returned in a typesafe way so that it
* can be thrown easily.
*
* @param <T> the type of the parent exception
* @param exception the exception to initialize
* @param cause the cause
* @return the exception with its cause initialized
*
* @throws IllegalArgumentException if an attempt is made to set
* an exception as its own cause
* @throws IllegalStateException if the exception has already had
* its cause initialized
* @see Throwable#initCause(Throwable)
*/
static <T extends Throwable> T
initCause(T exception, Throwable cause) {
exception.initCause(cause);
return exception;
}
/**
* Returns an {@link IllegalStateException} indicating that the
* given exception was not expected, and setting the cause to
* that exception.
*
* @param exception the unexpected exception
* @return an IllegalStateException
*/
static IllegalStateException unexpected(Throwable exception) {
return new IllegalStateException("unexpected exception" +
(exception.getMessage() == null
? ""
: ": " + exception.getMessage()),
exception);
}
/**
* Returns a new, completed {@link Future} with the given result.
*
* @param <V> the type of the result
* @param result the result for the returned {@code Future}
* @return a new, completed {@code Future} with the given result
*/
static <V> Future<V> finishedFuture(V result) {
return new ResultFuture<V>(result);
}
/**
* Returns a new, completed {@link Future} that always throws the
* given exception when its {@code get} methods are called.
*
* @param <V> the type of the result
* @param exception the exception for the returned {@code Future}
* to throw
* @return a new, completed {@code Future} that throws the exception
*/
static <V> Future<V> failedFuture(Throwable exception) {
return new FailedFuture<V>(exception);
}
/**
* Base class for an already-finished Future.
*
* @param <V> the result type
*/
abstract static class DoneFuture<V> implements Future<V> {
/** Allows construction by a subclass. */
protected DoneFuture() { }
/**
* {@inheritDoc}
* <p>
* This implementation calls {@code get()}, and never throws
* {@code InterruptedException} or {@code TimeoutException}.
*/
public V get(long timeout, TimeUnit unit) throws ExecutionException {
return get();
}
/**
* {@inheritDoc}
* <p>
* Never throws {@code InterruptedException}.
*/
public abstract V get() throws ExecutionException;
/**
* {@inheritDoc}
* <p>
* This implementation always returns {@code false}.
*/
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
/**
* {@inheritDoc}
* <p>
* This implementation always returns {@code false}.
*/
public boolean isCancelled() {
return false;
}
/**
* {@inheritDoc}
* <p>
* This implementation always returns {@code true}.
*/
public boolean isDone() {
return true;
}
}
/**
* A future that has already completed normally.
*
* @param <V> the result type
*/
private static final class ResultFuture<V> extends DoneFuture<V> {
/** The result to return from get() */
private final V result;
/**
* Creates a new, completed {@link Future} with the given result.
*
* @param result the result of this {@code Future}
*/
ResultFuture(V result) {
this.result = result;
}
/**
* {@inheritDoc}
* <p>
* This implementation always returns the result immediately.
*/
@Override
public V get() {
return result;
}
}
/**
* A future that has already completed by throwing an execution
* exception.
*
* @param <V> the result type
*/
private static final class FailedFuture<V> extends DoneFuture<V> {
/** The exception to throw from get() */
private final Throwable exception;
/**
* Creates a new, completed {@link Future} with the given exception.
*
* @param exception the exception to wrap with an
* {@link ExecutionException} and throw from this
* {@code Future}'s {@link Future#get() get} methods.
*/
FailedFuture(Throwable exception) {
if (exception == null) {
throw new NullPointerException("exception is null");
}
this.exception = exception;
}
/**
* {@inheritDoc}
* <p>
* Always throws the exception this future was constructed with.
*/
@Override
public V get() throws ExecutionException {
throw new ExecutionException(exception);
}
}
// Support formatting SelectionKey ops
/** A table of string representations of SelectionKey op combinations. */
private static final String[] opsTable = new String[] {
"", "C", "R", "CR", "W", "CW", "RW", "CRW",
"A", "AC", "AR", "ACR", "AW", "ACW", "ARW", "ACRW"
};
/**
* Returns a concise string representation of the active
* {@link SelectionKey} operations set in the parameter {@literal ops}.
* For example, if {@literal ops} is {@code OP_READ | OP_WRITE}, this
* method returns the string "RW".
*
* @param ops the {@code SelectionKey} operations to format
* @return a string representation of the active ops
* @see SelectionKey
*/
static String formatOps(int ops) {
return opsTable[(((ops & OP_CONNECT) != 0) ? 1 : 0) +
(((ops & OP_READ) != 0) ? 2 : 0) +
(((ops & OP_WRITE) != 0) ? 4 : 0) +
(((ops & OP_ACCEPT) != 0) ? 8 : 0)];
}
/**
* Returns the human-readable name of the given {@link SelectionKey}
* operation.
*
* @param op a {@link SelectionKey} operation
* @return a human-readable string, such as "OP_READ"
* @throws IllegalArgumentException if the op is not one of
* the operation constants defined by {@link SelectionKey}
* @see SelectionKey
*/
static String opName(int op) {
switch (op) {
case OP_READ:
return "OP_READ";
case OP_WRITE:
return "OP_WRITE";
case OP_CONNECT:
return "OP_CONNECT";
case OP_ACCEPT:
return "OP_ACCEPT";
default:
throw new IllegalArgumentException("Unknown opcode " + op);
}
}
}