001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the 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
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.hadoop.crypto.key;
019
020 import java.io.IOException;
021 import java.security.NoSuchAlgorithmException;
022 import java.util.concurrent.ExecutionException;
023 import java.util.concurrent.TimeUnit;
024
025 import com.google.common.cache.CacheBuilder;
026 import com.google.common.cache.CacheLoader;
027 import com.google.common.cache.LoadingCache;
028
029 /**
030 * A <code>KeyProviderExtension</code> implementation providing a short lived
031 * cache for <code>KeyVersions</code> and <code>Metadata</code>to avoid burst
032 * of requests to hit the underlying <code>KeyProvider</code>.
033 */
034 public class CachingKeyProvider extends
035 KeyProviderExtension<CachingKeyProvider.CacheExtension> {
036
037 static class CacheExtension implements KeyProviderExtension.Extension {
038 private final KeyProvider provider;
039 private LoadingCache<String, KeyVersion> keyVersionCache;
040 private LoadingCache<String, KeyVersion> currentKeyCache;
041 private LoadingCache<String, Metadata> keyMetadataCache;
042
043 CacheExtension(KeyProvider prov, long keyTimeoutMillis,
044 long currKeyTimeoutMillis) {
045 this.provider = prov;
046 keyVersionCache =
047 CacheBuilder.newBuilder().expireAfterAccess(keyTimeoutMillis,
048 TimeUnit.MILLISECONDS)
049 .build(new CacheLoader<String, KeyVersion>() {
050 @Override
051 public KeyVersion load(String key) throws Exception {
052 KeyVersion kv = provider.getKeyVersion(key);
053 if (kv == null) {
054 throw new KeyNotFoundException();
055 }
056 return kv;
057 }
058 });
059 keyMetadataCache =
060 CacheBuilder.newBuilder().expireAfterAccess(keyTimeoutMillis,
061 TimeUnit.MILLISECONDS)
062 .build(new CacheLoader<String, Metadata>() {
063 @Override
064 public Metadata load(String key) throws Exception {
065 Metadata meta = provider.getMetadata(key);
066 if (meta == null) {
067 throw new KeyNotFoundException();
068 }
069 return meta;
070 }
071 });
072 currentKeyCache =
073 CacheBuilder.newBuilder().expireAfterWrite(currKeyTimeoutMillis,
074 TimeUnit.MILLISECONDS)
075 .build(new CacheLoader<String, KeyVersion>() {
076 @Override
077 public KeyVersion load(String key) throws Exception {
078 KeyVersion kv = provider.getCurrentKey(key);
079 if (kv == null) {
080 throw new KeyNotFoundException();
081 }
082 return kv;
083 }
084 });
085 }
086 }
087
088 @SuppressWarnings("serial")
089 private static class KeyNotFoundException extends Exception { }
090
091 public CachingKeyProvider(KeyProvider keyProvider, long keyTimeoutMillis,
092 long currKeyTimeoutMillis) {
093 super(keyProvider, new CacheExtension(keyProvider, keyTimeoutMillis,
094 currKeyTimeoutMillis));
095 }
096
097 @Override
098 public KeyVersion getCurrentKey(String name) throws IOException {
099 try {
100 return getExtension().currentKeyCache.get(name);
101 } catch (ExecutionException ex) {
102 Throwable cause = ex.getCause();
103 if (cause instanceof KeyNotFoundException) {
104 return null;
105 } else if (cause instanceof IOException) {
106 throw (IOException) cause;
107 } else {
108 throw new IOException(cause);
109 }
110 }
111 }
112
113 @Override
114 public KeyVersion getKeyVersion(String versionName)
115 throws IOException {
116 try {
117 return getExtension().keyVersionCache.get(versionName);
118 } catch (ExecutionException ex) {
119 Throwable cause = ex.getCause();
120 if (cause instanceof KeyNotFoundException) {
121 return null;
122 } else if (cause instanceof IOException) {
123 throw (IOException) cause;
124 } else {
125 throw new IOException(cause);
126 }
127 }
128 }
129
130 @Override
131 public void deleteKey(String name) throws IOException {
132 getKeyProvider().deleteKey(name);
133 getExtension().currentKeyCache.invalidate(name);
134 getExtension().keyMetadataCache.invalidate(name);
135 // invalidating all key versions as we don't know
136 // which ones belonged to the deleted key
137 getExtension().keyVersionCache.invalidateAll();
138 }
139
140 @Override
141 public KeyVersion rollNewVersion(String name, byte[] material)
142 throws IOException {
143 KeyVersion key = getKeyProvider().rollNewVersion(name, material);
144 getExtension().currentKeyCache.invalidate(name);
145 getExtension().keyMetadataCache.invalidate(name);
146 return key;
147 }
148
149 @Override
150 public KeyVersion rollNewVersion(String name)
151 throws NoSuchAlgorithmException, IOException {
152 KeyVersion key = getKeyProvider().rollNewVersion(name);
153 getExtension().currentKeyCache.invalidate(name);
154 getExtension().keyMetadataCache.invalidate(name);
155 return key;
156 }
157
158 @Override
159 public Metadata getMetadata(String name) throws IOException {
160 try {
161 return getExtension().keyMetadataCache.get(name);
162 } catch (ExecutionException ex) {
163 Throwable cause = ex.getCause();
164 if (cause instanceof KeyNotFoundException) {
165 return null;
166 } else if (cause instanceof IOException) {
167 throw (IOException) cause;
168 } else {
169 throw new IOException(cause);
170 }
171 }
172 }
173
174 }