com.marklogic.xcc.impl.handlers.EvalRequestController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of marklogic-xcc Show documentation
Show all versions of marklogic-xcc Show documentation
MarkLogic XML Contentbase Connector for Java (XCC/J)
/*
* Copyright (c) 2021 MarkLogic Corporation
*
* 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 com.marklogic.xcc.impl.handlers;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.marklogic.http.HttpChannel;
import com.marklogic.xcc.Request;
import com.marklogic.xcc.RequestOptions;
import com.marklogic.xcc.ResultSequence;
import com.marklogic.xcc.exceptions.RequestException;
import com.marklogic.xcc.exceptions.RequestServerException;
import com.marklogic.xcc.impl.SessionImpl;
import com.marklogic.xcc.spi.ConnectionProvider;
import com.marklogic.xcc.spi.ServerConnection;
public class EvalRequestController extends AbstractRequestController {
private static final Map handlers = new HashMap(8);
static {
addDefaultHandler(handlers, new UnrecognizedCodeHandler());
addHandler(handlers, HttpURLConnection.HTTP_UNAVAILABLE, new ServiceUnavailableHandler());
addHandler(handlers, HttpURLConnection.HTTP_BAD_GATEWAY, new ServiceUnavailableHandler());
addHandler(handlers, HttpURLConnection.HTTP_GATEWAY_TIMEOUT, new ServiceUnavailableHandler());
addHandler(handlers, HttpURLConnection.HTTP_INTERNAL_ERROR, new ServerExceptionHandler());
addHandler(handlers, HttpURLConnection.HTTP_UNAUTHORIZED, new UnauthorizedHandler());
addHandler(handlers, HttpURLConnection.HTTP_NOT_FOUND, new NotFoundCodeHandler());
addHandler(handlers, HttpURLConnection.HTTP_BAD_REQUEST, new NotFoundCodeHandler());
addHandler(handlers, HttpURLConnection.HTTP_OK, new GoodQueryResponseHandler());
}
// --------------------------------------------------------
protected final String body;
private final String path;
// --------------------------------------------------------
public EvalRequestController(String path, String body) {
super(handlers);
this.path = path;
this.body = body;
}
// --------------------------------------------------------
// Invoked by superclass template method
@Override
public ResultSequence serverDialog(ServerConnection connection, Request request, RequestOptions options,
Logger logger) throws IOException, RequestException {
SessionImpl session = (SessionImpl)request.getSession();
//DAL: convert to byte array here so we get the right size for the buffer
byte[] bodyBytes = body.getBytes("UTF-8");
HttpChannel http = buildChannel(connection, path, session, options, bodyBytes.length, logger);
issueRequest(http, bodyBytes , logger);
int code = http.getResponseCode();
if (logger.isLoggable(Level.FINE)) {
logger.fine("response: " + code + " (" + http.getResponseMessage() + ")");
}
setConnectionTimeout(connection, http);
// bug:57063/DBI-50 ALB may close the connection.
// Close the connection if we see connection:close header
String connectionClose = http.getResponseConnection();
if (connectionClose != null && connectionClose.equalsIgnoreCase("close")) {
connection.setTimeoutTime(0);
if (logger.isLoggable(Level.FINE))
logger.fine("Connection:closed header seen. Set connection timeout to 0...");
}
session.setServerVersion(http.getServerVersion());
if (logger.isLoggable(Level.FINE)) {
logger.fine("server version: " + session.getServerVersion());
}
if (!session.readCookieValues(http)) {
String version = session.getServerVersion();
throw new RequestServerException("Incompatible server version "
+ version == null ? "" : version +
". Make sure to set xcc.txn.compatible to true", request);
}
// Bug 55499, 55728: The error code for XDMP-MODNOTFOUND has been mapped
// from 500 to 404. To correctly parse the error node returned by the
// server, xcc should still use ServerExceptionHandler instead of
// NotFoundCodeHandler.
if (http.getResponseMessage().contains("Module Not Found")) {
return (ResultSequence)findHandler(
HttpURLConnection.HTTP_INTERNAL_ERROR).
handleResponse(http, code, request, connection, logger);
}
return (ResultSequence)findHandler(code).handleResponse(http, code,
request, connection, logger);
}
// --------------------------------------------------------
@Override
protected long interTryDelay(long delay, int currentTry) {
if ((currentTry == 0) || (delay <= 0)) {
return 0;
}
return delay;
}
// --------------------------------------------------------
private HttpChannel buildChannel(ServerConnection connection, String path, SessionImpl session,
RequestOptions options, int bodyLength, Logger logger) {
String method = "POST";
HttpChannel http = new HttpChannel(connection.channel(), method, path,
bodyLength , options.getTimeoutMillis(), logger);
http.setRequestContentType("application/x-www-form-urlencoded");
addCommonHeaders(http, session, method, path, options, logger);
if (bodyLength > http.getBufferSize() && HttpChannel.isUseHTTP()) {
http.setRequestHeader("Transfer-Encoding", "chunked");
} else {
http.setCloseOutputIfNoContentLength(true);
}
return (http);
}
// -----------------------------------------------------
private void issueRequest(HttpChannel http,
byte[] encodedQueryBytes, // encoded as UTF8 bytes
Logger logger
) throws IOException {
if (logger.isLoggable(Level.FINE)) {
// DAL do not dump extrodinary long queries
final int kMAX_QUERY_SIZE = 1024*10;
int len = encodedQueryBytes.length;
if( len < kMAX_QUERY_SIZE )
logger.fine("encoded query: " + new String(encodedQueryBytes,"UTF-8") );
else
logger.fine("encoded query [" + ( len - kMAX_QUERY_SIZE ) + " bytes truncated]: "
+ new String( encodedQueryBytes , 0 ,kMAX_QUERY_SIZE , "UTF-8" ));
}
int bufSize = http.getBufferSize();
int offset = 0;
int remaining = encodedQueryBytes.length;
boolean isChunked = remaining > bufSize && HttpChannel.isUseHTTP();
while (remaining > 0) {
int length = Math.min(remaining, bufSize);
if (isChunked) {
writeChunkHeader(http, DATA_CHUNK, length, logger);
}
http.write(encodedQueryBytes, offset, length);
if (isChunked) {
http.write("\r\n".getBytes(StandardCharsets.US_ASCII));
}
remaining -= length;
offset += length;
}
if (isChunked) {
writeChunkHeader(http, DATA_CHUNK, 0, logger);
}
}
}