/** * * Copyright (c) Microsoft and contributors. All rights reserved. * * 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 com.microsoft.azure.keyvault.extensions; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.ListenableFuture; import com.microsoft.azure.keyvault.core.IKey; import com.microsoft.azure.keyvault.core.IKeyResolver; /** * The collection of key resolvers that would iterate on a key id to resolve to {@link IKey}. */ public class AggregateKeyResolver implements IKeyResolver { /** * Future key class that resolves a key id after the async result is available. */ class FutureKey extends AbstractFuture<IKey> { private final String kid; private boolean isCancelled = false; private boolean isDone = false; private IKey result = null; FutureKey(String kid) { this.kid = kid; } @Override public boolean cancel(boolean mayInterruptIfRunning) { // mark cancelled isCancelled = true; return isCancelled; } @Override public boolean isCancelled() { return isCancelled; } @Override public boolean isDone() { // always true return isDone; } @Override public IKey get() throws InterruptedException, ExecutionException { // throw if cancelled if (isCancelled) { throw new InterruptedException(); } synchronized (resolvers) { for (IKeyResolver resolver : resolvers) { Future<IKey> futureKey = resolver.resolveKeyAsync(kid); result = futureKey.get(); if (result != null) { break; } } } // Mark done isDone = true; return result; } @Override public IKey get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { // throw if cancelled if (isCancelled) { throw new InterruptedException(); } synchronized (resolvers) { for (IKeyResolver resolver : resolvers) { Future<IKey> futureKey = resolver.resolveKeyAsync(kid); result = futureKey.get(timeout, unit); if (result != null) { break; } } } // Mark done isDone = true; return result; } } private final List<IKeyResolver> resolvers; /** * Constructor. */ public AggregateKeyResolver() { resolvers = Collections.synchronizedList(new ArrayList<IKeyResolver>()); } /** * Adds a key resolver to the collection of key resolvers. * @param resolver the key resolver */ public void add(IKeyResolver resolver) { synchronized (resolvers) { resolvers.add(resolver); } } @Override public ListenableFuture<IKey> resolveKeyAsync(String kid) { return new FutureKey(kid); } }