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.keymanager.channel; 019 020 import java.util.Collections; 021 import java.util.HashMap; 022 import java.util.HashSet; 023 import java.util.Map; 024 import java.util.Set; 025 026 import org.cumulus4j.keymanager.SessionManager; 027 import org.cumulus4j.keymanager.back.shared.GetActiveEncryptionKeyRequest; 028 import org.cumulus4j.keymanager.back.shared.GetKeyRequest; 029 import org.cumulus4j.keymanager.back.shared.Request; 030 031 /** 032 * <p> 033 * Manager for the communication channel between key manager and application server. 034 * </p> 035 * <p> 036 * The so-called "key manager channel" is - as shown in the document 037 * <a target="_blank" href="http://cumulus4j.org/1.0.1/documentation/deployment-scenarios.html">Deployment scenarios</a> - an 038 * HTTP(S) connection from the key-manager to the application server with an inverse request-response-cycle. 039 * This means, the application server sends a {@link org.cumulus4j.keymanager.back.shared.Request}, 040 * the key manager handles it and then sends a {@link org.cumulus4j.keymanager.back.shared.Response} back. 041 * </p> 042 * 043 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de 044 */ 045 public class KeyManagerChannelManager 046 { 047 private SessionManager sessionManager; 048 private String appServerBaseURL; 049 private String keyManagerChannelURL; 050 private int desiredThreadCount; 051 052 private Set<KeyManagerChannelListenerThread> listenerThreads = Collections.synchronizedSet(new HashSet<KeyManagerChannelListenerThread>()); 053 054 private static final Map<Class<? extends Request>, Class<? extends RequestHandler<?>>> requestClass2handlerClass; 055 static { 056 Map<Class<? extends Request>, Class<? extends RequestHandler<?>>> m = new HashMap<Class<? extends Request>, Class<? extends RequestHandler<?>>>(); 057 m.put(GetKeyRequest.class, GetKeyRequestHandler.class); 058 m.put(GetActiveEncryptionKeyRequest.class, GetActiveEncryptionKeyRequestHandler.class); 059 requestClass2handlerClass = Collections.unmodifiableMap(m); 060 } 061 062 /** 063 * Instantiate a <code>KeyManagerChannelManager</code>. 064 * 065 * @param sessionManager the {@link SessionManager} which 066 * @param appServerBaseURL the base-URL before the "/KeyManagerChannel" - e.g. if the REST URL of the KeyManagerChannel-service is 067 * "https://serverUsingCumulus4j.mydomain.org/org.cumulus4j.keymanager.back.webapp/KeyManagerChannel", then this must be 068 * "https://serverUsingCumulus4j.mydomain.org/org.cumulus4j.keymanager.back.webapp". 069 */ 070 public KeyManagerChannelManager(SessionManager sessionManager, String appServerBaseURL) 071 { 072 if (sessionManager == null) 073 throw new IllegalArgumentException("sessionManager == null"); 074 075 if (appServerBaseURL == null) 076 throw new IllegalArgumentException("appServerBaseURL == null"); 077 078 this.sessionManager = sessionManager; 079 080 this.appServerBaseURL = appServerBaseURL; 081 082 String s = appServerBaseURL.toString(); 083 if (!s.endsWith("/")) 084 s += '/'; 085 086 this.keyManagerChannelURL = s + "KeyManagerChannel"; 087 088 setDesiredThreadCount(5); // TODO make this manage itself automatically according to load statistics 089 } 090 091 /** 092 * Get the {@link SessionManager} that was passed in the constructor. 093 * @return the {@link SessionManager}. 094 */ 095 public SessionManager getSessionManager() { 096 return sessionManager; 097 } 098 099 /** 100 * Get the base-URL before the "/KeyManagerChannel" - e.g. if the REST URL of the KeyManagerChannel-service is 101 * "https://serverUsingCumulus4j.mydomain.org/org.cumulus4j.keymanager.back.webapp/KeyManagerChannel", then this must be 102 * "https://serverUsingCumulus4j.mydomain.org/org.cumulus4j.keymanager.back.webapp". 103 * @return the base-URL before the "/KeyManagerChannel". 104 */ 105 public String getAppServerBaseURL() { 106 return appServerBaseURL; 107 } 108 109 /** 110 * Get the complete URL to the <code>KeyManagerChannel</code>. 111 * 112 * @return the complete URL to the <code>KeyManagerChannel</code>. 113 */ 114 public String getKeyManagerChannelURL() { 115 return keyManagerChannelURL; 116 } 117 118 /** 119 * <p> 120 * Set the quantity of {@link KeyManagerChannelListenerThread}s that should be running for this 121 * {@link KeyManagerChannelManager}. 122 * </p> 123 * <p> 124 * If the given <code>desiredThreadCount</code> is greater than 125 * the number of currently running threads, new threads are created. If the <code>desiredThreadCount</code> 126 * is less than the number of currently running threads, some of the threads will terminate themselves 127 * until the number of currently running threads matches the desired quantity. 128 * </p> 129 * @param desiredThreadCount the new quantity of {@link KeyManagerChannelListenerThread}s which should be 130 * active for this {@link KeyManagerChannelManager}. 131 * @see #getDesiredThreadCount() 132 */ 133 public void setDesiredThreadCount(int desiredThreadCount) { 134 this.desiredThreadCount = desiredThreadCount; 135 while (listenerThreads.size() < desiredThreadCount) { 136 KeyManagerChannelListenerThread thread = new KeyManagerChannelListenerThread(this); 137 listenerThreads.add(thread); 138 thread.start(); 139 } 140 } 141 142 /** 143 * Get the quantity of {@link KeyManagerChannelListenerThread}s that should be running for this 144 * {@link KeyManagerChannelManager}. 145 * @return the quantity of {@link KeyManagerChannelListenerThread}s that should be active for this 146 * {@link KeyManagerChannelManager}. 147 * @see #setDesiredThreadCount(int) 148 */ 149 public int getDesiredThreadCount() { 150 return desiredThreadCount; 151 } 152 153 /** 154 * <p> 155 * Unregister the given <code>thread</code>, if there are currently more threads running than desired. 156 * </p> 157 * <p> 158 * This method is called by a {@link KeyManagerChannelListenerThread} in its run-loop to determine, if the thread 159 * should terminate itself (see {@link #setDesiredThreadCount(int)}). If the method returns <code>true</code>, 160 * the thread will exit its {@link Thread#run() run()} method. 161 * </p> 162 * @param thread the thread. 163 * @return <code>true</code> if the thread was unregistered and thus must exit its <code>run()</code> method; 164 * <code>false</code> if the thread was not unregistered and should thus continue. 165 */ 166 protected boolean unregisterThreadIfMoreThanDesiredThreadCount(KeyManagerChannelListenerThread thread) 167 { 168 synchronized (listenerThreads) { 169 if (listenerThreads.size() > desiredThreadCount) { 170 listenerThreads.remove(thread); 171 return true; 172 } 173 else 174 return false; 175 } 176 } 177 178 /** 179 * Get the appropriate {@link RequestHandler handler} for the given <code>request</code>. 180 * 181 * @param <R> the type of the <code>request</code>. 182 * @param request the request. 183 * @return the {@link RequestHandler} for the request. 184 * @throws InstantiationException if {@link Class#newInstance()} failed to create the handler instance. 185 * @throws IllegalAccessException if {@link Class#newInstance()} failed to create the handler instance. 186 */ 187 protected <R extends Request> RequestHandler<R> getRequestHandler(R request) 188 throws InstantiationException, IllegalAccessException 189 { 190 if (request == null) 191 throw new IllegalArgumentException("request == null"); 192 193 Class<? extends Request> requestClass = request.getClass(); 194 Class<? extends RequestHandler<?>> handlerClass = requestClass2handlerClass.get(requestClass); 195 if (handlerClass == null) 196 throw new IllegalStateException("There is no RequestHandler class registered for this requestClass: " + requestClass); 197 198 @SuppressWarnings("unchecked") 199 RequestHandler<R> requestHandler = (RequestHandler<R>) handlerClass.newInstance(); 200 requestHandler.setKeyManagerChannelManager(this); 201 return requestHandler; 202 } 203 }