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.keymanager.messagebroker;
019
020 import java.util.concurrent.TimeoutException;
021
022 import org.cumulus4j.keymanager.back.shared.ErrorResponse;
023 import org.cumulus4j.keymanager.back.shared.GetKeyRequest;
024 import org.cumulus4j.keymanager.back.shared.GetKeyResponse;
025 import org.cumulus4j.keymanager.back.shared.Message;
026 import org.cumulus4j.keymanager.back.shared.NullResponse;
027 import org.cumulus4j.keymanager.back.shared.Request;
028 import org.cumulus4j.keymanager.back.shared.Response;
029 import org.cumulus4j.store.crypto.CryptoSession;
030 import org.cumulus4j.store.crypto.keymanager.rest.ErrorResponseException;
031
032 /**
033 * <p>
034 * Broker transmitting {@link Message messages} between application-server and key-manager.
035 * </p>
036 * <p>
037 * As documented in <a target="_blank" href="http://cumulus4j.org/1.2.0-SNAPSHOT/documentation/deployment-scenarios.html">Deployment scenarios</a>,
038 * TCP connections are always established from the key-manager (i.e. client or key-server) to the application server.
039 * Since this means that the key-exchange-request-response-cycle works opposite the HTTP-request-response-cycle,
040 * we need this <code>MessageBroker</code>.
041 * </p>
042 * <p>
043 * Within every JVM, there is one single {@link MessageBrokerRegistry#getActiveMessageBroker() active MessageBroker}.
044 * This instance must make sure that messages can be exchanged from every cluster-node to every key-manager; i.e. if
045 * the key-manager connects to a different cluster-node than the primary connection (established by the application logic),
046 * the {@link Request}s must be proxied over the right cluster-node to the key-manager. The {@link Response} must
047 * of course be routed appropriately back to the correct cluster-node:
048 * </p>
049 * <p>
050 * <img src="http://cumulus4j.org/1.2.0-SNAPSHOT/images/deployment-scenario/deployment-scenario-without-keyserver-with-cluster.png" />
051 * </p>
052 * <p>
053 * <b>Important:</b> You should not directly implement this interface but instead subclass {@link AbstractMessageBroker}!
054 * </p>
055 *
056 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
057 */
058 public interface MessageBroker
059 {
060 /**
061 * <p>
062 * System property to control the timeout (in milliseconds) for the {@link #query(Class, Request)} method.
063 * </p>
064 * <p>
065 * The <code>query(...)</code> method will throw a {@link TimeoutException}, if no {@link Response}
066 * to a given {@link Request} arrived within this timeout.
067 * </p>
068 * <p>
069 * If the system property is not present or not a valid number, it is up to the <code>MessageBroker</code>
070 * implementation, what default value should be used. See {@link AbstractMessageBroker#getQueryTimeout()}
071 * for the default implemented there.
072 * </p>
073 */
074 static final String SYSTEM_PROPERTY_QUERY_TIMEOUT = "cumulus4j.MessageBroker.queryTimeout";
075
076 /**
077 * <p>
078 * System property to control the timeout (in milliseconds) for the {@link #pollRequest(String)} method.
079 * </p>
080 * <p>
081 * The <code>pollRequest(...)</code> method returns <code>null</code>, if no {@link Request} popped up
082 * in the to-do-queue within the timeout.
083 * </p>
084 * <p>
085 * If the system property is not present or not a valid number, it is up to the <code>MessageBroker</code>
086 * implementation, what default value should be used. See {@link AbstractMessageBroker#getPollRequestTimeout()}
087 * for the default implemented there.
088 * </p>
089 */
090 static final String SYSTEM_PROPERTY_POLL_REQUEST_TIMEOUT = "cumulus4j.MessageBroker.pollRequestTimeout";
091
092 // ActiveKeyManagerChannelRegistration registerActiveKeyManagerChannel(String cryptoSessionIDPrefix, String internalKeyManagerChannelURL);
093 //
094 // void unregisterActiveKeyManagerChannel(ActiveKeyManagerChannelRegistration registration);
095
096 /**
097 * <p>
098 * Send <code>request</code> to the key-manager (embedded in client or separate in key-server) and return its response.
099 * </p>
100 * <p>
101 * This method is used for example by a {@link CryptoSession} to request keys via a {@link GetKeyRequest}. As soon as
102 * this method entered with the <code>request</code>, it is expected that the {@link #pollRequest(String)} returns
103 * this <code>request</code> to the appropriate key-manager. The <code>query(...)</code> method blocks then until
104 * the key-manager handled the <code>request</code> and sent a {@link GetKeyResponse} back. As soon as the <code>response</code>
105 * was {@link #pushResponse(Response) pushed} into the <code>MessageBroker</code>, <code>query(...)</code> should return it.
106 * </p>
107 * <p>
108 * If the expected {@link Response} does not arrive within the query-timeout (configurable via
109 * system property {@value #SYSTEM_PROPERTY_QUERY_TIMEOUT}), this method should throw
110 * a {@link TimeoutException}.
111 * </p>
112 *
113 * @param responseClass the type of the expected response; can be null, if you expect to receive null (i.e. you pass a "void" request).
114 * @param request the request to be sent to the key-manager.
115 * @return the response from the key-manager. Will be <code>null</code>, if the key-manager replied with a {@link NullResponse}.
116 * @throws TimeoutException if the request was not replied within the {@link #SYSTEM_PROPERTY_QUERY_TIMEOUT query-timeout}.
117 * @throws ErrorResponseException if the key-manager (either running embedded on the remote client or
118 * in a separate key-server) sent an {@link ErrorResponse}.
119 */
120 <R extends Response> R query(Class<R> responseClass, Request request)
121 throws TimeoutException, ErrorResponseException;
122
123 /**
124 * <p>
125 * Poll the next {@link Request} that is waiting to be processed.
126 * </p>
127 * <p>
128 * This method is - indirectly via a REST web-service - called by the key-manager periodically
129 * in order to receive requests. If there is a request waiting, this method should immediately
130 * return it. If there is no request in the queue, this method should wait for an incoming
131 * request for a short time. If there is still no request available after a short blocking time,
132 * this method should return <code>null</code> (before the remote client would timeout).
133 * </p>
134 * <p>
135 * Usually, blocking about 1 minute is recommended in most situations. However, when
136 * using certain runtimes, it must be much shorter (e.g. the Google App Engine allows
137 * requests not to take longer than 30 sec, thus 20 sec are an appropriate time to stay safe).
138 * </p>
139 * <p>
140 * Additionally, since the remote key-manager must wait at maximum this time, its HTTP-client's
141 * timeout must be longer than this timeout.
142 * </p>
143 * <p>
144 * It should be possible to configure this timeout via the system property
145 * {@value #SYSTEM_PROPERTY_POLL_REQUEST_TIMEOUT}. Implementors should use
146 * {@link #getPollRequestTimeout()} for this purpose.
147 * </p>
148 * @param cryptoSessionIDPrefix usually, every key-manager uses the same prefix for
149 * all crypto-sessions. Thus, this prefix is used to efficiently route requests to
150 * the right key-manager.
151 * @return the next request waiting for processing and fitting to the given <code>cryptoSessionIDPrefix</code>
152 * or <code>null</code>, if no such request pops up in the to-do-queue within the timeout.
153 */
154 Request pollRequest(String cryptoSessionIDPrefix);
155
156 /**
157 * <p>
158 * Push a {@link Response} in order to reply a previous request.
159 * </p>
160 * <p>
161 * This method is - indirectly via a REST web-service - called by the key-manager after
162 * it successfully handled a {@link Request}.
163 * </p>
164 * @param response the response answering a previous {@link Request} enqueued by {@link #query(Class, Request)}.
165 */
166 void pushResponse(Response response);
167
168 /**
169 * <p>
170 * Get the {@link MessageBroker#pollRequest(String) pollRequest(....)} timeout in milliseconds.
171 * </p>
172 * <p>
173 * This method takes the system property {@link MessageBroker#SYSTEM_PROPERTY_POLL_REQUEST_TIMEOUT} into account.
174 * If the system property is not present or not a valid number, the default value 60000 (1 minute) is returned.
175 * </p>
176 * <p>
177 * Usually, a value of about 1 minute is recommended in most situations. However, when
178 * using certain runtimes, it must be much shorter (e.g. the Google App Engine allows
179 * requests not to take longer than 30 sec, thus 20 sec are an appropriate time to stay safe).
180 * </p>
181 * <p>
182 * Additionally, since the remote key-manager must wait at maximum this time, its HTTP-client's
183 * timeout must be longer than this timeout.
184 * </p>
185 *
186 * @return the {@link MessageBroker#pollRequest(String) pollRequest(....)} timeout in milliseconds.
187 */
188 long getPollRequestTimeout();
189
190 /**
191 * <p>
192 * Get the {@link MessageBroker#query(Class, Request) query} timeout in milliseconds.
193 * </p>
194 * <p>
195 * This method takes the system property {@link MessageBroker#SYSTEM_PROPERTY_QUERY_TIMEOUT} into account.
196 * If the system property is not present or not a valid number, the default value 300000 (5 minutes) is returned.
197 * </p>
198 *
199 * @return the {@link MessageBroker#query(Class, Request) query} timeout in milliseconds.
200 */
201 long getQueryTimeout();
202 }