001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2022, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.jwk.source;
019
020
021import java.io.Closeable;
022import java.util.List;
023import java.util.Objects;
024
025import net.jcip.annotations.ThreadSafe;
026
027import com.nimbusds.jose.KeySourceException;
028import com.nimbusds.jose.jwk.JWK;
029import com.nimbusds.jose.jwk.JWKSelector;
030import com.nimbusds.jose.proc.SecurityContext;
031import com.nimbusds.jose.util.IOUtils;
032
033
034/**
035 * JWK source with optional failover.
036 *
037 * @author Thomas Rørvik Skjølberg
038 * @author Vladimir Dzhuvinov
039 * @version 2022-08-24
040 */
041@ThreadSafe
042public class JWKSourceWithFailover<C extends SecurityContext> implements JWKSource<C>, Closeable {
043        
044        private final JWKSource<C> jwkSource;
045        private final JWKSource<C> failoverJWKSource;
046
047        
048        /**
049         * Creates a new JWK source with optional failover.
050         *
051         * @param jwkSource         The primary JWK source. Must not be
052         *                          {@code null}.
053         * @param failoverJWKSource Optional failover JWK source if retrieval
054         *                          from the primary JWK source fails,
055         *                          {@code null} if no failover.
056         */
057        public JWKSourceWithFailover(final JWKSource<C> jwkSource, final JWKSource<C> failoverJWKSource) {
058                Objects.requireNonNull(jwkSource, "The primary JWK source must not be null");
059                this.jwkSource = jwkSource;
060                this.failoverJWKSource = failoverJWKSource;
061        }
062
063        
064        /**
065         * Fails over to the configured JWK source.
066         */
067        private List<JWK> failover(final Exception exception, final JWKSelector jwkSelector, final C context)
068                throws KeySourceException {
069
070                try {
071                        return failoverJWKSource.get(jwkSelector, context);
072                } catch (KeySourceException kse) {
073                        throw new KeySourceException(
074                                exception.getMessage() + "; Failover JWK source retrieval failed with: " + kse.getMessage(), kse
075                        );
076                }
077        }
078        
079
080        @Override
081        public List<JWK> get(final JWKSelector jwkSelector, final C context)
082                throws KeySourceException {
083                
084                try {
085                        return jwkSource.get(jwkSelector, context);
086                } catch (Exception e) {
087                        return failover(e, jwkSelector, context);
088                }
089        }
090
091        
092        @Override
093        public void close() {
094                if (jwkSource instanceof Closeable) {
095                        IOUtils.closeSilently((Closeable)jwkSource);
096                }
097                if (failoverJWKSource instanceof Closeable) {
098                        IOUtils.closeSilently((Closeable)failoverJWKSource);
099                }
100        }
101}