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

org.xnio.IoUtils Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2008 Red Hat, Inc. and/or its affiliates.
 *
 * 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.xnio;

import java.io.Closeable;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.Selector;
import java.nio.channels.Channel;
import java.nio.channels.WritableByteChannel;
import java.util.Random;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.zip.ZipFile;
import org.xnio.channels.SuspendableReadChannel;

import java.util.logging.Handler;

import static org.xnio._private.Messages.closeMsg;
import static org.xnio._private.Messages.msg;

/**
 * General I/O utility methods.
 *
 * @apiviz.exclude
 */
public final class IoUtils {

    private static final Executor NULL_EXECUTOR = new Executor() {
        private final String string = String.format("null executor <%s>", Integer.toHexString(hashCode()));

        public void execute(final Runnable command) {
            // no operation
        }

        public String toString() {
            return string;
        }
    };
    private static final Executor DIRECT_EXECUTOR = new Executor() {
        private final String string = String.format("direct executor <%s>", Integer.toHexString(hashCode()));

        public void execute(final Runnable command) {
            command.run();
        }

        public String toString() {
            return string;
        }
    };
    private static final Closeable NULL_CLOSEABLE = new Closeable() {
        private final String string = String.format("null closeable <%s>", Integer.toHexString(hashCode()));
        public void close() throws IOException {
            // no operation
        }

        public String toString() {
            return string;
        }
    };
    private static final Cancellable NULL_CANCELLABLE = new Cancellable() {
        public Cancellable cancel() {
            return this;
        }
    };
    @SuppressWarnings("rawtypes")
    private static final IoUtils.ResultNotifier RESULT_NOTIFIER = new IoUtils.ResultNotifier();

    private IoUtils() {}

    /**
     * Get the direct executor.  This is an executor that executes the provided task in the same thread.
     *
     * @return a direct executor
     */
    public static Executor directExecutor() {
        return DIRECT_EXECUTOR;
    }

    /**
     * Get the null executor.  This is an executor that never actually executes the provided task.
     *
     * @return a null executor
     */
    public static Executor nullExecutor() {
        return NULL_EXECUTOR;
    }

    /**
     * Get the null closeable.  This is a simple {@code Closeable} instance that does nothing when its {@code close()}
     * method is invoked.
     *
     * @return the null closeable
     */
    public static Closeable nullCloseable() {
        return NULL_CLOSEABLE;
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final AutoCloseable resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (ClosedChannelException ignored) {
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final Closeable resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (ClosedChannelException ignored) {
            msg.tracef("safeClose, ignoring ClosedChannelException exception");
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a series of resources, logging errors if they occur.
     *
     * @param resources the resources to close
     */
    public static void safeClose(final Closeable... resources) {
        for (Closeable resource : resources) {
            safeClose(resource);
        }
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final Socket resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (ClosedChannelException ignored) {
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final DatagramSocket resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final Selector resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (ClosedChannelException ignored) {
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final ServerSocket resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (ClosedChannelException ignored) {
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final ZipFile resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a resource, logging an error if an error occurs.
     *
     * @param resource the resource to close
     */
    public static void safeClose(final Handler resource) {
        try {
            if (resource != null) {
                closeMsg.closingResource(resource);
                resource.close();
            }
        } catch (Throwable t) {
            closeMsg.resourceCloseFailed(t, resource);
        }
    }

    /**
     * Close a future resource, logging an error if an error occurs.  Attempts to cancel the operation if it is
     * still in progress.
     *
     * @param futureResource the resource to close
     */
    public static void safeClose(final IoFuture futureResource) {
        if (futureResource != null) {
            futureResource.cancel().addNotifier(closingNotifier(), null);
        }
    }

    private static final IoFuture.Notifier ATTACHMENT_CLOSING_NOTIFIER = new IoFuture.Notifier() {
        public void notify(final IoFuture future, final Closeable attachment) {
            IoUtils.safeClose(attachment);
        }
    };

    private static final IoFuture.Notifier CLOSING_NOTIFIER = new IoFuture.HandlingNotifier() {
        public void handleDone(final Closeable result, final Void attachment) {
            IoUtils.safeClose(result);
        }
    };

    /**
     * Get a notifier that closes the attachment.
     *
     * @return a notifier which will close its attachment
     */
    public static IoFuture.Notifier attachmentClosingNotifier() {
        return ATTACHMENT_CLOSING_NOTIFIER;
    }

    /**
     * Get a notifier that closes the result.
     *
     * @return a notifier which will close the result of the operation (if successful)
     */
    public static IoFuture.Notifier closingNotifier() {
        return CLOSING_NOTIFIER;
    }

    /**
     * Get a notifier that runs the supplied action.
     *
     * @param runnable the notifier type
     * @param  the future type (not used)
     * @return a notifier which will run the given command
     */
    public static  IoFuture.Notifier runnableNotifier(final Runnable runnable) {
        return new IoFuture.Notifier() {
            public void notify(final IoFuture future, final Void attachment) {
                runnable.run();
            }
        };
    }

    /**
     * Get the result notifier.  This notifier will forward the result of the {@code IoFuture} to the attached
     * {@code Result}.
     *
     * @param  the result type
     * @return the notifier
     */
    @SuppressWarnings({ "unchecked" })
    public static  IoFuture.Notifier> resultNotifier() {
        return RESULT_NOTIFIER;
    }

    /**
     * Get the notifier that invokes the channel listener given as an attachment.
     *
     * @param  the channel type
     * @return the notifier
     */
    @SuppressWarnings({ "unchecked" })
    public static  IoFuture.Notifier> channelListenerNotifier() {
        return CHANNEL_LISTENER_NOTIFIER;
    }

    @SuppressWarnings("rawtypes")
    private static final IoFuture.Notifier CHANNEL_LISTENER_NOTIFIER = new IoFuture.HandlingNotifier>() {
        @SuppressWarnings({ "unchecked" })
        public void handleDone(final Channel channel, final ChannelListener channelListener) {
            channelListener.handleEvent(channel);
        }
    };

    /**
     * Get a {@code java.util.concurrent}-style {@code Future} instance wrapper for an {@code IoFuture} instance.
     *
     * @param ioFuture the {@code IoFuture} to wrap
     * @return a {@code Future}
     */
    public static  Future getFuture(final IoFuture ioFuture) {
        return new Future() {
            public boolean cancel(final boolean mayInterruptIfRunning) {
                ioFuture.cancel();
                return ioFuture.await() == IoFuture.Status.CANCELLED;
            }

            public boolean isCancelled() {
                return ioFuture.getStatus() == IoFuture.Status.CANCELLED;
            }

            public boolean isDone() {
                return ioFuture.getStatus() == IoFuture.Status.DONE;
            }

            public T get() throws InterruptedException, ExecutionException {
                try {
                    return ioFuture.getInterruptibly();
                } catch (IOException e) {
                    throw new ExecutionException(e);
                }
            }

            public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
                try {
                    if (ioFuture.awaitInterruptibly(timeout, unit) == IoFuture.Status.WAITING) {
                        throw msg.opTimedOut();
                    }
                    return ioFuture.getInterruptibly();
                } catch (IOException e) {
                    throw new ExecutionException(e);
                }
            }

            public String toString() {
                return String.format("java.util.concurrent.Future wrapper <%s> for %s", Integer.toHexString(hashCode()), ioFuture);
            }
        };
    }

    private static final IoFuture.Notifier COUNT_DOWN_NOTIFIER = new IoFuture.Notifier() {
        public void notify(final IoFuture future, final CountDownLatch latch) {
            latch.countDown();
        }
    };

    /**
     * Wait for all the futures to complete.
     *
     * @param futures the futures to wait for
     */
    public static void awaitAll(IoFuture... futures) {
        final int len = futures.length;
        final CountDownLatch cdl = new CountDownLatch(len);
        for (IoFuture future : futures) {
            future.addNotifier(COUNT_DOWN_NOTIFIER, cdl);
        }
        boolean intr = false;
        try {
            while (cdl.getCount() > 0L) {
                try {
                    cdl.await();
                } catch (InterruptedException e) {
                    intr = true;
                }
            }
        } finally {
            if (intr) {
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * Wait for all the futures to complete.
     *
     * @param futures the futures to wait for
     * @throws InterruptedException if the current thread is interrupted while waiting
     */
    public static void awaitAllInterruptibly(IoFuture... futures) throws InterruptedException {
        final int len = futures.length;
        final CountDownLatch cdl = new CountDownLatch(len);
        for (IoFuture future : futures) {
            future.addNotifier(COUNT_DOWN_NOTIFIER, cdl);
        }
        cdl.await();
    }

    /**
     * Create an {@code IoFuture} which wraps another {@code IoFuture}, but returns a different type.
     *
     * @param parent the original {@code IoFuture}
     * @param type the class of the new {@code IoFuture}
     * @param  the type of the original result
     * @param  the type of the wrapped result
     * @return a wrapper {@code IoFuture}
     */
    public static  IoFuture cast(final IoFuture parent, final Class type) {
        return new CastingIoFuture(parent, type);
    }

    /**
     * Safely shutdown reads on the given channel.
     *
     * @param channel the channel
     */
    public static void safeShutdownReads(final SuspendableReadChannel channel) {
        if (channel != null) {
            try {
                channel.shutdownReads();
            } catch (IOException e) {
                closeMsg.resourceReadShutdownFailed(null, null);
            }
        }
    }

    /**
     * Platform-independent channel-to-channel transfer method.  Uses regular {@code read} and {@code write} operations
     * to move bytes from the {@code source} channel to the {@code sink} channel.  After this call, the {@code throughBuffer}
     * should be checked for remaining bytes; if there are any, they should be written to the {@code sink} channel before
     * proceeding.  This method may be used with NIO channels, XNIO channels, or a combination of the two.
     * 

* If either or both of the given channels are blocking channels, then this method may block. * * @param source the source channel to read bytes from * @param count the number of bytes to transfer (must be >= {@code 0L}) * @param throughBuffer the buffer to transfer through (must not be {@code null}) * @param sink the sink channel to write bytes to * @return the number of bytes actually transferred (possibly 0) * @throws IOException if an I/O error occurs during the transfer of bytes */ public static long transfer(final ReadableByteChannel source, final long count, final ByteBuffer throughBuffer, final WritableByteChannel sink) throws IOException { long res; long total = 0L; throughBuffer.limit(0); while (total < count) { throughBuffer.compact(); try { if (count - total < (long) throughBuffer.remaining()) { throughBuffer.limit((int) (count - total)); } res = source.read(throughBuffer); if (res <= 0) { return total == 0L ? res : total; } } finally { throughBuffer.flip(); } res = sink.write(throughBuffer); if (res == 0) { return total; } total += res; } return total; } // nested classes private static class CastingIoFuture implements IoFuture { private final IoFuture parent; private final Class type; private CastingIoFuture(final IoFuture parent, final Class type) { this.parent = parent; this.type = type; } public IoFuture cancel() { parent.cancel(); return this; } public Status getStatus() { return parent.getStatus(); } public Status await() { return parent.await(); } public Status await(final long time, final TimeUnit timeUnit) { return parent.await(time, timeUnit); } public Status awaitInterruptibly() throws InterruptedException { return parent.awaitInterruptibly(); } public Status awaitInterruptibly(final long time, final TimeUnit timeUnit) throws InterruptedException { return parent.awaitInterruptibly(time, timeUnit); } public O get() throws IOException, CancellationException { return type.cast(parent.get()); } public O getInterruptibly() throws IOException, InterruptedException, CancellationException { return type.cast(parent.getInterruptibly()); } public IOException getException() throws IllegalStateException { return parent.getException(); } public IoFuture addNotifier(final Notifier notifier, final A attachment) { parent.addNotifier(new Notifier() { public void notify(final IoFuture future, final A attachment) { notifier.notify(CastingIoFuture.this, attachment); } }, attachment); return this; } } /** * Get a notifier which forwards the result to another {@code IoFuture}'s manager. * * @param the channel type * @return the notifier */ @SuppressWarnings({ "unchecked" }) public static IoFuture.Notifier> getManagerNotifier() { return MANAGER_NOTIFIER; } @SuppressWarnings("rawtypes") private static final ManagerNotifier MANAGER_NOTIFIER = new ManagerNotifier(); private static class ManagerNotifier extends IoFuture.HandlingNotifier> { public void handleCancelled(final FutureResult manager) { manager.setCancelled(); } public void handleFailed(final IOException exception, final FutureResult manager) { manager.setException(exception); } public void handleDone(final T result, final FutureResult manager) { manager.setResult(result); } } /** * A channel source which tries to acquire a channel from a delegate channel source the given number of times before * giving up. * * @param delegate the delegate channel source * @param maxTries the number of times to retry * @param the channel type * @return the retrying channel source */ public static ChannelSource getRetryingChannelSource(final ChannelSource delegate, final int maxTries) throws IllegalArgumentException { if (maxTries < 1) { throw msg.minRange("maxTries", 1); } return new RetryingChannelSource(maxTries, delegate); } private static class RetryingNotifier extends IoFuture.HandlingNotifier> { private volatile int remaining; private final int maxTries; private final Result result; private final ChannelSource delegate; private final ChannelListener openListener; RetryingNotifier(final int maxTries, final Result result, final ChannelSource delegate, final ChannelListener openListener) { this.maxTries = maxTries; this.result = result; this.delegate = delegate; this.openListener = openListener; remaining = maxTries; } public void handleFailed(final IOException exception, final Result attachment) { if (remaining-- == 0) { result.setException(new IOException("Failed to create channel after " + maxTries + " tries", exception)); return; } tryOne(attachment); } public void handleCancelled(final Result attachment) { result.setCancelled(); } public void handleDone(final T data, final Result attachment) { result.setResult(data); } void tryOne(final Result attachment) { final IoFuture ioFuture = delegate.open(openListener); ioFuture.addNotifier(this, attachment); } } private static class RetryingChannelSource implements ChannelSource { private final int maxTries; private final ChannelSource delegate; RetryingChannelSource(final int maxTries, final ChannelSource delegate) { this.maxTries = maxTries; this.delegate = delegate; } public IoFuture open(final ChannelListener openListener) { final FutureResult result = new FutureResult(); final IoUtils.RetryingNotifier notifier = new IoUtils.RetryingNotifier(maxTries, result, delegate, openListener); notifier.tryOne(result); return result.getIoFuture(); } } /** * A cancellable which closes the given resource on cancel. * * @param c the resource * @return the cancellable */ public static Cancellable closingCancellable(final Closeable c) { return new ClosingCancellable(c); } private static class ClosingCancellable implements Cancellable { private final Closeable c; ClosingCancellable(final Closeable c) { this.c = c; } public Cancellable cancel() { safeClose(c); return this; } } /** * Get the null cancellable. * * @return the null cancellable */ public static Cancellable nullCancellable() { return NULL_CANCELLABLE; } private static class ResultNotifier extends IoFuture.HandlingNotifier> { public void handleCancelled(final Result result) { result.setCancelled(); } public void handleFailed(final IOException exception, final Result result) { result.setException(exception); } public void handleDone(final T value, final Result result) { result.setResult(value); } } /** * Get a thread-local RNG. Do not share this instance with other threads. * * @return the thread-local RNG */ public static Random getThreadLocalRandom() { return ThreadLocalRandom.current(); } }