org.gridgain.client.impl.connection.GridClientHttpConnection Maven / Gradle / Ivy
/*
Copyright (C) GridGain Systems. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* _________ _____ __________________ _____
* __ ____/___________(_)______ /__ ____/______ ____(_)_______
* _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \
* / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / /
* \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/
*/
package org.gridgain.client.impl.connection;
import net.sf.json.*;
import org.gridgain.client.*;
import org.gridgain.client.impl.*;
import org.gridgain.client.util.*;
import org.gridgain.grid.kernal.processors.rest.client.message.*;
import org.jetbrains.annotations.*;
import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.logging.*;
/**
* Java client implementation.
*/
public class GridClientHttpConnection extends GridClientConnection {
/** Logger. */
private static final Logger log = Logger.getLogger(GridClientHttpConnection.class.getName());
/** Thread pool. */
private final ExecutorService pool;
/** Reason why connection was closed. {@code null} means connection is still alive. */
private GridClientConnectionCloseReason closeReason;
/** Busy lock for graceful close. */
private ReadWriteLock busyLock = new ReentrantReadWriteLock();
/** Pending requests */
private GridConcurrentHashSet pendingReqs = new GridConcurrentHashSet<>();
/** Session token. */
private String sesTok;
/** Connection timeout. */
private final int connTimeout;
/** Read timeout. */
private final int readTimeout;
/**
* Creates client.
*
* @param clientId Client identifier.
* @param srvAddr Server address on which HTTP REST handler resides.
* @param sslCtx SSL context to use if SSL is enabled, {@code null} otherwise.
* @param connTimeout Connection timeout.
* @param readTimeout Read timeout.
* @param top Topology to use.
* @param pool Thread pool executor.
* @param cred Client credentials.
* @throws IOException If input-output error occurs.
*/
public GridClientHttpConnection(UUID clientId, InetSocketAddress srvAddr, SSLContext sslCtx, int connTimeout,
int readTimeout, GridClientTopology top, ExecutorService pool, Object cred) throws IOException {
super(clientId, srvAddr, sslCtx, top, cred);
this.connTimeout = connTimeout;
this.readTimeout = readTimeout;
Socket sock = new Socket();
try {
sock.connect(srvAddr, connTimeout);
}
finally {
GridClientUtils.closeQuiet(sock);
}
if (log.isLoggable(Level.INFO))
log.info("Client HTTP connection established: " + serverAddress());
this.pool = pool;
}
/** {@inheritDoc} */
@Override void close(GridClientConnectionCloseReason reason, boolean waitCompletion) {
busyLock.writeLock().lock();
try {
if (closeReason != null)
return;
closeReason = reason;
}
finally {
busyLock.writeLock().unlock();
}
if (waitCompletion) {
Iterator tasks = pendingReqs.iterator();
try {
while (tasks.hasNext()) {
FutureWorker worker = tasks.next();
worker.awaitCompletion();
tasks.remove();
}
}
catch (InterruptedException ignored) {
log.warning("Interrupted while waiting for all pending requests to complete (will cancel remaining " +
"requests): " + serverAddress());
Thread.currentThread().interrupt();
}
}
if (log.isLoggable(Level.FINE))
log.fine("Cancelling " + pendingReqs.size() + " pending requests: " + serverAddress());
Iterator tasks = pendingReqs.iterator();
while (tasks.hasNext()) {
FutureWorker worker = tasks.next();
worker.cancel();
tasks.remove();
}
if (log.isLoggable(Level.INFO))
log.info("Client HTTP connection closed: " + serverAddress());
}
/** {@inheritDoc} */
@Override boolean closeIfIdle(long idleTimeout) {
return false;
}
/**
* Creates new future and passes it to the makeJettyRequest.
*
* @param params Request parameters.
* @param flags Cache flags to be enabled.
* @param destNodeId Destination node ID.
* @return Future.
* @throws GridClientClosedException If client was manually closed.
* @throws GridClientConnectionResetException If connection could not be established.
*/
private GridClientFutureAdapter makeJettyRequest(Map params,
Collection flags, UUID destNodeId)
throws GridClientClosedException, GridClientConnectionResetException {
int flagsBitMap = encodeCacheFlags(flags);
if (flagsBitMap != 0)
params.put("cacheFlags", Integer.toString(flagsBitMap));
return makeJettyRequest(params, destNodeId);
}
/**
* Makes request to Jetty server.
*
* @param params Parameters map.
* @param destNodeId Destination node ID.
* @return Response.
* @throws GridClientConnectionResetException In connection to the server can not be established.
* @throws GridClientClosedException If connection was closed manually.
*/
@SuppressWarnings("unchecked")
private GridClientFutureAdapter makeJettyRequest(final Map params, final UUID destNodeId)
throws GridClientConnectionResetException, GridClientClosedException {
assert params != null;
assert params.containsKey("cmd");
busyLock.readLock().lock();
try {
checkClosed(closeReason);
try {
final GridClientFutureAdapter fut = new GridClientFutureAdapter<>();
final URLConnection urlConn = openConnection(params, destNodeId);
FutureWorker worker = new FutureWorker(fut) {
@Override protected void body() throws Exception {
try {
InputStream input = urlConn.getInputStream();
// Read reply and close input stream.
JSONObject json = readReply(input);
int okStatus = json.getInt("successStatus");
if (okStatus == GridClientResponse.STATUS_AUTH_FAILURE) {
sesTok = null;
InputStream inputAuth = openConnection(params, destNodeId).getInputStream();
// Read reply and close input stream.
json = readReply(inputAuth);
}
if (json.getString("sessionToken") != null)
sesTok = json.getString("sessionToken");
okStatus = json.getInt("successStatus");
String errMsg = (String)json.get("error");
if (okStatus == GridClientResponse.STATUS_AUTH_FAILURE) {
sesTok = null;
fut.onDone(new GridClientAuthenticationException("Client authentication failed " +
"[clientId=" + clientId + ", srvAddr=" + serverAddress() + ", errMsg=" + errMsg +
']'));
}
else if (okStatus == GridClientResponse.STATUS_FAILED) {
if (errMsg == null || errMsg.isEmpty())
errMsg = "Unknown server error.";
fut.onDone(new GridClientException(errMsg));
}
else if (okStatus != GridClientResponse.STATUS_SUCCESS) {
fut.onDone(new GridClientException("Unsupported server response status code" +
": " + okStatus));
}
else {
Object res = json.get("response");
if (JSONNull.getInstance().equals(res))
res = null;
fut.onDone((R)res);
}
}
catch (IOException e) {
fut.onDone(new GridClientConnectionResetException(
"Failed to perform request (connection failed): " + serverAddress(), e));
}
catch (Throwable e) {
fut.onDone(new GridClientException(
"Failed to perform request: " + serverAddress(), e));
}
finally {
pendingReqs.remove(this);
}
}
@Override protected void cancelBody() {
fut.onDone(new GridClientClosedException("Failed to perform request (connection was closed" +
" before response was received): " + serverAddress()));
}
};
pendingReqs.add(worker);
pool.execute(worker);
return fut;
}
catch (RejectedExecutionException ignored) {
throw new GridClientClosedException(
"Client was closed (no public methods of client can be used anymore).");
}
catch (IOException e) {
throw new GridClientConnectionResetException("Failed to read response from remote server: " +
serverAddress(), e);
}
}
finally {
busyLock.readLock().unlock();
}
}
/**
* Opens url-connection with specified request parameters.
*
* @param params Request parameters.
* @param destId Destination ID, if {@code null} - no destination.
* @return New opened connection.
* @throws IOException If connection could not be established.
*/
private URLConnection openConnection(Map params, @Nullable UUID destId) throws IOException {
Map hdr = new HashMap<>();
Map body = new HashMap<>(params);
hdr.put("clientId", clientId.toString());
if (destId != null)
hdr.put("destId", destId.toString());
if (sesTok != null)
hdr.put("sessionToken", sesTok);
else if (credentials() != null)
body.put("cred", credentials());
// Build request uri.
String uri = (sslContext() == null ? "http://" : "https://") +
serverAddress().getHostName() + ':' + serverAddress().getPort() +
"/gridgain?" + encodeParams(hdr);
HttpURLConnection conn = (HttpURLConnection)new URL(uri).openConnection();
conn.setConnectTimeout(connTimeout);
conn.setReadTimeout(readTimeout);
if (sslContext() != null) {
((HttpsURLConnection)conn).setSSLSocketFactory(sslContext().getSocketFactory());
// Work-around for SSL connections limit in 32 open persistent connections.
conn.setRequestProperty("Connection", "Close");
}
// Provide POST body.
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept-Charset", "UTF-8");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
OutputStream output = null;
try {
// Initiate connection request.
output = conn.getOutputStream();
output.write(encodeParams(body).toString().getBytes("UTF-8"));
output.flush();
}
finally {
GridClientUtils.closeQuiet(output);
}
// Initiate connection response.
return conn;
}
/**
* Encode parameters map into "application/x-www-form-urlencoded" format.
*
* @param params Parameters map to encode.
* @return Form-encoded parameters.
* @throws IOException In case of error.
*/
private StringBuilder encodeParams(Map params) throws IOException {
StringBuilder data = new StringBuilder();
for (Map.Entry e : params.entrySet()) {
if (!(e.getValue() instanceof String))
throw new IllegalArgumentException("Http connection supports only string arguments in requests" +
", while received [key=" + e.getKey() + ", value=" + e.getValue() + "]");
if (data.length() > 0)
data.append('&');
data.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=')
.append(URLEncoder.encode((String)e.getValue(), "UTF-8"));
}
return data;
}
/**
* Reads input stream contents, parses JSON object and closes input stream.
*
* @param input Input stream to read from.
* @return JSON object parsed from input stream.
* @throws IOException If input read failed.
*/
private JSONObject readReply(InputStream input) throws IOException {
return JSONObject.fromObject(readRawReply(input));
}
/**
* Reads input stream content as a string and closes input stream.
*
* @param input Input to read from.
* @return Content of the stream.
* @throws IOException If input read failed.
*/
private String readRawReply(InputStream input) throws IOException {
try {
final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
StringBuilder buf = new StringBuilder();
String line;
while ((line = reader.readLine()) != null)
buf.append(line);
return buf.toString();
}
finally {
input.close();
}
}
/** {@inheritDoc} */
@Override public GridClientFutureAdapter cachePutAll(String cacheName, Map entries,
Set flags, UUID destNodeId)
throws GridClientConnectionResetException, GridClientClosedException {
assert entries != null;
Map params = new HashMap<>();
params.put("cmd", "putall");
if (cacheName != null)
params.put("cacheName", cacheName);
int i = 1;
for (Map.Entry e : entries.entrySet()) {
params.put("k" + i, e.getKey());
params.put("v" + i, e.getValue());
i++;
}
return makeJettyRequest(params, flags, destNodeId);
}
/** {@inheritDoc} */
@Override public GridClientFutureAdapter cacheGet(String cacheName, K key, Set flags,
UUID destNodeId) throws GridClientConnectionResetException, GridClientClosedException {
return makeCacheRequest("get", cacheName, key, null, flags, destNodeId);
}
/** {@inheritDoc} */
@Override public GridClientFutureAdapter cachePut(String cacheName, K key, V val,
Set flags, UUID destNodeId)
throws GridClientConnectionResetException, GridClientClosedException {
return makeCacheRequest("put", cacheName, key, val, flags, destNodeId);
}
/** {@inheritDoc} */
@Override public GridClientFutureAdapter
© 2015 - 2025 Weber Informatics LLC | Privacy Policy