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 */ 018package org.apache.hadoop.crypto.key; 019 020import java.io.IOException; 021import java.security.NoSuchAlgorithmException; 022import java.util.concurrent.ExecutionException; 023import java.util.concurrent.TimeUnit; 024 025import com.google.common.cache.CacheBuilder; 026import com.google.common.cache.CacheLoader; 027import 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 */ 034public 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}