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 }