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

org.jtrim2.access.AccessTokens Maven / Gradle / Ivy

package org.jtrim2.access;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.jtrim2.cancel.CancellationToken;
import org.jtrim2.concurrent.Tasks;
import org.jtrim2.event.ListenerRef;
import org.jtrim2.event.ListenerRefs;
import org.jtrim2.utils.ExceptionHelper;

/**
 * A utility class containing static convenience methods for
 * {@link AccessToken AccessTokens}.
 */
public final class AccessTokens {
    private AccessTokens() {
        throw new AssertionError();
    }

    /**
     * Registers a listener to be notified when all of the provided tokens
     * were released.
     * 

* The listener will be notified even if the specified tokens were already * released and will not be notified more than once. The listener must * expect to be called from various threads, including the current thread * (i.e.: from within this {@code addReleaseAllListener} method call). * *

Unregistering the listener

* Unlike the general {@code removeXXX} idiom in Swing listeners, this * listener can be removed using the returned reference. *

* The unregistering of the listener is not necessary, the listener will * automatically be unregistered once it has been notified. * * @param tokens the collection of {@link AccessToken access tokens} to be * checked when they will be released. When all of these tokens * are released, the listener will be notified. This argument cannot be * {@code null} and cannot contain {@code null} elements. This collection * is allowed to be empty, in which case the listener will be notified * immediately in this method call. * @param listener the {@code Runnable} whose {@code run} method will be * invoked when all the specified access tokens are released. This * argument cannot be {@code null}. * @return the reference to the newly registered listener which can be * used to remove this newly registered listener, so it will no longer * be notified of the release event. Note that this method may return * an unregistered listener if all the access tokens were already released * prior to this method call. This method never returns {@code null}. * * @throws NullPointerException thrown if any of the arguments is * {@code null} or any of the specified tokens in the collection is * {@code null} */ public static ListenerRef addReleaseAllListener( Collection> tokens, final Runnable listener) { ExceptionHelper.checkNotNullElements(tokens, "tokens"); Objects.requireNonNull(listener, "listener"); final Collection listenerRefs = new ArrayList<>(); final AtomicInteger activeCount = new AtomicInteger(1); for (AccessToken token: tokens) { activeCount.incrementAndGet(); ListenerRef listenerRef = token.addReleaseListener(() -> { if (activeCount.decrementAndGet() == 0) { listener.run(); } }); listenerRefs.add(listenerRef); } if (activeCount.decrementAndGet() == 0) { listener.run(); } ListenerRef[] refArray = listenerRefs.toArray(new ListenerRef[listenerRefs.size()]); return ListenerRefs.combineListenerRefs(refArray); } /** * Registers a listener to be notified when at least one of the provided * tokens has been released. The listener will not be notified multiple * times. The listener must expect to be called from various threads, * including the current thread (i.e.: from within this * {@code addReleaseAllListener} method call). * *

Unregistering the listener

* Unlike the general {@code removeXXX} idiom in Swing listeners, this * listener can be removed using the returned reference. *

* The unregistering of the listener is not necessary, the listener will * automatically be unregistered once it has been notified. * * @param tokens the collection of {@link AccessToken access tokens} to be * checked when they will be released. When at least one of these * tokens is released, the listener will be notified. This argument cannot be * {@code null} and cannot contain {@code null} elements. This collection * is allowed to be empty, in which case the listener will never be * notified. * @param listener the {@code Runnable} whose {@code run} method will be * invoked when all the specified access tokens are released. This * argument cannot be {@code null}. * @return the reference to the newly registered listener which can be * used to remove this newly registered listener, so it will no longer * be notified of the release event. Note that this method may return * an unregistered listener. This method never returns {@code null}. * * @throws NullPointerException thrown if any of the arguments is * {@code null} or any of the specified tokens in the collection is * {@code null} */ public static ListenerRef addReleaseAnyListener( Collection> tokens, final Runnable listener) { ExceptionHelper.checkNotNullElements(tokens, "tokens"); Objects.requireNonNull(listener, "listener"); final AtomicReference listenerRefsRef = new AtomicReference<>(null); final ListenerRef unregisterAll = () -> { ListenerRef[] refs = listenerRefsRef.getAndSet(null); if (refs != null) { for (ListenerRef ref: refs) { ref.unregister(); } } }; final AtomicBoolean released = new AtomicBoolean(false); Collection listenerRefs = new ArrayList<>(tokens.size()); Runnable idempotentListener = Tasks.runOnceTask(() -> { released.set(true); listener.run(); unregisterAll.unregister(); }); for (AccessToken token: tokens) { ListenerRef listenerRef = token.addReleaseListener(idempotentListener); listenerRefs.add(listenerRef); } listenerRefsRef.set(listenerRefs.toArray(new ListenerRef[listenerRefs.size()])); if (released.get()) { idempotentListener.run(); } return unregisterAll; } /** * Creates a new independent {@link AccessToken}. *

* The returned token does not conflict with any other {@code AccessToken}s * and will function as a general purpose {@code AccessToken}. * * @param the type of {@link AccessToken#getAccessID() ID} * associated with the returned {@link AccessToken} * @param id the {@link AccessToken#getAccessID() ID} * associated with the returned {@link AccessToken}. This argument * cannot be {@code null}. * @return the newly created {@link AccessToken}. This method never returns * {@code null}. * * @throws NullPointerException thrown if any of the arguments is * {@code null} */ public static AccessToken createToken(IDType id) { return new GenericAccessToken<>(id); } /** * Creates a new {@link AccessToken} from two underlying * {@code AccessToken}s which will only execute tasks if both the passed * {@code AccessToken}s allow tasks to be executed. The returned token * effectively represents a token that is associated with the rights of * both passed {@code AccessToken}s. Releasing the returned token will * release both tokens passed in the arguments but the returned token is * considered released even if only one of the underlying tokens is * released. *

* Note that the order of the passed {@code AccessToken}s does not * matter. *

* Note that the resulting token will submit tasks to executors of both * specified tokens to detect * {@link AccessManager#getScheduledAccess(AccessRequest) scheduled tokens}. * These tasks are not the same as the ones passed to the resulting token. * * @param the type of {@link AccessToken#getAccessID() ID} * of the returned {@link AccessToken} * @param id the access ID of the returned access token. This argument * cannot be {@code null}. * @param tokens the tokens to be combined. This argument cannot be * {@code null} and cannot contain {@code null} elements. Also this * array must contain at least a single element. * @return the new combined {@link AccessToken}. This method never returns * {@code null}. * * @throws IllegalArgumentException thrown if there are zero tokens * specified in the arguments * @throws NullPointerException thrown if any of the arguments is * {@code null} */ public static AccessToken combineTokens(IDType id, AccessToken... tokens) { return new CombinedToken<>(id, tokens); } /** * Calls {@link AccessToken#releaseAndCancel() releaseAndCancel()} on all of * the passed {@link AccessToken AccessTokens}. * @param tokens the {@link AccessToken AccessTokens} to be released. * This argument cannot be {@code null}. * * @throws NullPointerException thrown if the argument or one of the * tokens are {@code null}. Note that even if this exception is thrown * some tokens might have already been released by this method. */ public static void releaseAndCancelTokens(AccessToken... tokens) { releaseAndCancelTokens(Arrays.asList(tokens)); } /** * Calls {@link AccessToken#releaseAndCancel() releaseAndCancel()} on all of * the passed {@link AccessToken AccessTokens}. * * @param tokens the {@link AccessToken AccessTokens} to be released. * This argument cannot be {@code null}. * * @throws NullPointerException thrown if the argument or one of the * tokens are {@code null}. Note that even if this exception is thrown * some tokens might have already been released by this method. */ public static void releaseAndCancelTokens(Collection> tokens) { for (AccessToken token: tokens) { token.releaseAndCancel(); } } /** * Releases the conflicting tokens of the specified * {@link AccessResult AccessResults} and waits for them to be released. * This method first calls * {@link AccessToken#releaseAndCancel() releaseAndCancel()} on all the * conflicting tokens then waits for them to be released. *

* This is more efficient than releasing and waiting for the tokens * one-by-one because this way tokens may be released concurrently so it * takes roughly as much time as the slowest token needs. * * @param cancelToken the {@code CancellationToken} to be checked if this * call should return (by throwing an {@code OperationCanceledException} * immediately without waiting for the tokens to be released. * @param results the {@link AccessResult AccessResults} of which the * conflicting tokens need to be released. This argument cannot be * {@code null}. * * @throws NullPointerException thrown if the argument or one of the * {@link AccessResult AccessResults} are {@code null}. Note that even if * this exception is thrown some tokens might have already been released * by this method. */ public static void unblockResults( CancellationToken cancelToken, AccessResult... results) { unblockResults(cancelToken, Arrays.asList(results)); } /** * Releases the conflicting tokens of the specified * {@link AccessResult AccessResults} and waits for them to be released. * This method first calls * {@link AccessToken#releaseAndCancel() releaseAndCancel()} on all the * conflicting tokens then waits for them to be released. *

* This is more efficient than releasing and waiting for the tokens * one-by-one because this way tokens may be released concurrently so it * takes roughly as much time as the slowest token needs. * * @param cancelToken the {@code CancellationToken} to be checked if this * call should return (by throwing an {@code OperationCanceledException} * immediately without waiting for the tokens to be released. * @param results the {@link AccessResult AccessResults} of which the * conflicting tokens need to be released. This argument cannot be * {@code null}. * * @throws NullPointerException thrown if the argument or one of the * {@link AccessResult AccessResults} are {@code null}. Note that even if * this exception is thrown some tokens might have already been released * by this method. * @throws org.jtrim2.cancel.OperationCanceledException thrown if the * specified {@code CancellationToken} signals a cancellation request * before the specified tokens were released. The tokens were requested to * be released even if this exception is thrown. */ public static void unblockResults( CancellationToken cancelToken, Collection> results) { for (AccessResult result: results) { result.releaseAndCancelBlockingTokens(); } for (AccessResult result: results) { for (AccessToken token: result.getBlockingTokens()) { token.awaitRelease(cancelToken); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy