001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, 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.io.IOException;
023import java.util.List;
024import java.util.Objects;
025
026import net.jcip.annotations.ThreadSafe;
027
028import com.nimbusds.jose.KeySourceException;
029import com.nimbusds.jose.jwk.JWK;
030import com.nimbusds.jose.jwk.JWKSelector;
031import com.nimbusds.jose.jwk.JWKSet;
032import com.nimbusds.jose.proc.SecurityContext;
033
034
035/**
036 * JSON Web Key (JWK) set based JWK source.
037 *
038 * @author Thomas Rørvik Skjølberg
039 * @author Vladimir Dzhuvinov
040 * @version 2022-11-22
041 */
042@ThreadSafe
043public class JWKSetBasedJWKSource<C extends SecurityContext> implements JWKSource<C>, Closeable {
044
045        
046        private final JWKSetSource<C> source;
047        
048        
049        /**
050         * Creates a new JWK set based JWK source.
051         *
052         * @param source The JWK set source. Must not be {@code null}.
053         */
054        public JWKSetBasedJWKSource(final JWKSetSource<C> source) {
055                Objects.requireNonNull(source);
056                this.source = source;
057        }
058
059        
060        @Override
061        public List<JWK> get(final JWKSelector jwkSelector, final C context) throws KeySourceException {
062                
063                long currentTime = System.currentTimeMillis();
064                
065                // Get the list of JWKs and match against the selector.
066                // If no matches, attempt to refresh the list of JWKs
067                // and repeat the matching.
068                
069                // So for the no-match scenario, what we have is a
070                // read-write-read type transaction. In order to identify
071                // whether another thread has already performed the write operation,
072                // an evaluator for the original read operation is passed along
073                // and used internally to check whether the cache is up-to-date; preventing
074                // unnecessary external calls
075                
076                JWKSet jwkSet = source.getJWKSet(JWKSetCacheRefreshEvaluator.noRefresh(), currentTime, context);
077                
078                List<JWK> select = jwkSelector.select(jwkSet);
079                if (select.isEmpty()) {
080                        JWKSet recentJwkSet = source.getJWKSet(JWKSetCacheRefreshEvaluator.referenceComparison(jwkSet), currentTime, context);
081                        select = jwkSelector.select(recentJwkSet);
082                }
083                return select;
084        }
085        
086        /**
087         * Returns the underlying JWK set source.
088         *
089         * @return The JWK set source.
090         */
091        public JWKSetSource<C> getJWKSetSource() {
092                return source;
093        }
094        
095        
096        @Override
097        public void close() throws IOException {
098                source.close();
099        }
100}