001 /* 002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org 003 * Copyright (C) 2011 NightLabs Consulting GmbH 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 package org.cumulus4j.store.crypto; 019 020 import java.lang.ref.WeakReference; 021 import java.util.HashMap; 022 import java.util.Map; 023 import java.util.WeakHashMap; 024 025 import org.datanucleus.NucleusContext; 026 import org.slf4j.Logger; 027 import org.slf4j.LoggerFactory; 028 029 /** 030 * <p> 031 * Registry holding instances of {@link CryptoManager}. 032 * </p> 033 * <p> 034 * There is one JVM-singleton-instance of {@link CryptoManagerRegistry} per {@link NucleusContext}. 035 * Since it is held in a {@link WeakHashMap}, a <code>CryptoManagerRegistry</code> will be garbage-collected 036 * when the corresponding <code>NucleusContext</code> is "forgotten". 037 * </p> 038 * 039 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 040 */ 041 public class CryptoManagerRegistry 042 { 043 private static final Logger logger = LoggerFactory.getLogger(CryptoManagerRegistry.class); 044 private static Map<NucleusContext, CryptoManagerRegistry> nucleusContext2sharedInstance = new WeakHashMap<NucleusContext, CryptoManagerRegistry>(); 045 046 /** 047 * <p> 048 * Get the {@link CryptoManagerRegistry} corresponding to a given {@link NucleusContext}. 049 * </p> 050 * <p> 051 * If there is no registry known for the given <code>NucleusContext</code>, yet, it will be created and 052 * associated to this context. If this method is later on called again for the same <code>NucleusContext</code>, 053 * the same <code>CryptoManagerRegistry</code> will be returned. 054 * </p> 055 * <p> 056 * This method is thread-safe. 057 * </p> 058 * 059 * @param nucleusContext the <code>NucleusContext</code> for which to get the <code>CryptoManagerRegistry</code>. 060 * @return the <code>CryptoManagerRegistry</code> associated to the given <code>NucleusContext</code>; never <code>null</code>. 061 */ 062 public static CryptoManagerRegistry sharedInstance(NucleusContext nucleusContext) 063 { 064 synchronized (nucleusContext2sharedInstance) { 065 CryptoManagerRegistry registry = nucleusContext2sharedInstance.get(nucleusContext); 066 if (registry == null) { 067 registry = new CryptoManagerRegistry(nucleusContext); 068 nucleusContext2sharedInstance.put(nucleusContext, registry); 069 } 070 return registry; 071 } 072 } 073 074 private WeakReference<NucleusContext> nucleusContextRef; 075 076 private Map<String, CryptoManager> id2keyManager = new HashMap<String, CryptoManager>(); 077 078 private CryptoManagerRegistry(NucleusContext nucleusContext) 079 { 080 if (nucleusContext == null) 081 throw new IllegalArgumentException("nucleusContext == null"); 082 083 this.nucleusContextRef = new WeakReference<NucleusContext>(nucleusContext); 084 } 085 086 /** 087 * <p> 088 * Get a {@link CryptoManager} for the specified <code>cryptoManagerID</code>. 089 * </p> 090 * <p> 091 * Within the context of one <code>CryptoManagerRegistry</code> instance, this method will always 092 * return the same instance of <code>CryptoManager</code> for a certain <code>cryptoManagerID</code>. 093 * In other words, there is exactly one <code>CryptoManager</code> instance for each unique combination 094 * of {@link NucleusContext} and <code>cryptoManagerID</code>. 095 * </p> 096 * <p> 097 * This method is thread-safe. 098 * </p> 099 * 100 * @param cryptoManagerID the identifier used in the extension-declaration (in the <code>plugin.xml</code>). 101 * @return the {@link CryptoManager} for the specified <code>cryptoManagerID</code>; never <code>null</code>. 102 * @throws UnknownCryptoManagerIDException if there is no {@link CryptoManager} registered for the given identifier. 103 */ 104 public CryptoManager getCryptoManager(String cryptoManagerID) 105 throws UnknownCryptoManagerIDException 106 { 107 synchronized (id2keyManager) { 108 CryptoManager cryptoManager = id2keyManager.get(cryptoManagerID); 109 if (cryptoManager == null) { 110 cryptoManager = createCryptoManager(cryptoManagerID); 111 id2keyManager.put(cryptoManagerID, cryptoManager); 112 } 113 return cryptoManager; 114 } 115 } 116 117 /** 118 * <p> 119 * Get the {@link NucleusContext} for which this <code>CryptoManagerRegistry</code> 120 * has been created. 121 * </p> 122 * <p> 123 * This method returns <code>null</code>, if the 124 * <code>NucleusContext</code> has already been garbage-collected (the reference 125 * is kept as a {@link WeakReference}). 126 * </p> 127 * <p> 128 * <b>Important:</b> Hold the result of this method only in a stack variable (i.e. scope = method) 129 * or a {@link WeakReference}! Otherwise you run the risk of a memory leak!!! 130 * </p> 131 * @return the {@link NucleusContext} or <code>null</code>, if it was already garbage-collected. 132 * It is never <code>null</code> as long as the <code>NucleusContext</code> is still valid 133 * (i.e. not garbage-collected). 134 */ 135 public NucleusContext getNucleusContext() { 136 return nucleusContextRef.get(); 137 } 138 139 private CryptoManager createCryptoManager(String cryptoManagerID) 140 throws UnknownCryptoManagerIDException 141 { 142 CryptoManager cryptoManager; 143 try { 144 NucleusContext nucleusContext = getNucleusContext(); 145 146 cryptoManager = (CryptoManager) nucleusContext.getPluginManager().createExecutableExtension( 147 "org.cumulus4j.store.crypto_manager", 148 "crypto-manager-id", cryptoManagerID, 149 "class", 150 null, null 151 ); 152 } catch (Exception e) { 153 logger.error("Could not create CryptoManager from extension: " + e, e); 154 throw new RuntimeException(e); 155 } 156 157 if (cryptoManager == null) 158 throw new UnknownCryptoManagerIDException(cryptoManagerID); 159 160 cryptoManager.setCryptoManagerRegistry(this); 161 cryptoManager.setCryptoManagerID(cryptoManagerID); 162 163 return cryptoManager; 164 } 165 }