com.marklogic.client.impl.JerseyServices Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-client-api Show documentation
Show all versions of java-client-api Show documentation
The official MarkLogic Java client API.
/*
* Copyright 2012-2016 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.client.impl;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.net.URI;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.bind.DatatypeConverter;
import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
import org.apache.http.auth.params.AuthPNames;
import org.apache.http.client.HttpClient;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SchemeSocketFactory;
import org.apache.http.conn.ssl.AbstractVerifier;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.marklogic.client.DatabaseClientFactory;
import com.marklogic.client.DatabaseClientFactory.Authentication;
import com.marklogic.client.DatabaseClientFactory.SSLHostnameVerifier;
import com.marklogic.client.DatabaseClient;
import com.marklogic.client.FailedRequestException;
import com.marklogic.client.ForbiddenUserException;
import com.marklogic.client.MarkLogicIOException;
import com.marklogic.client.MarkLogicInternalException;
import com.marklogic.client.ResourceNotFoundException;
import com.marklogic.client.ResourceNotResendableException;
import com.marklogic.client.Transaction;
import com.marklogic.client.bitemporal.TemporalDescriptor;
import com.marklogic.client.document.ContentDescriptor;
import com.marklogic.client.document.DocumentDescriptor;
import com.marklogic.client.document.DocumentManager.Metadata;
import com.marklogic.client.document.DocumentPage;
import com.marklogic.client.document.DocumentRecord;
import com.marklogic.client.document.DocumentWriteOperation;
import com.marklogic.client.document.DocumentWriteSet;
import com.marklogic.client.document.DocumentUriTemplate;
import com.marklogic.client.document.ServerTransform;
import com.marklogic.client.eval.EvalResult;
import com.marklogic.client.eval.EvalResultIterator;
import com.marklogic.client.eval.ServerEvaluationCall;
import com.marklogic.client.extensions.ResourceServices.ServiceResult;
import com.marklogic.client.extensions.ResourceServices.ServiceResultIterator;
import com.marklogic.client.io.BytesHandle;
import com.marklogic.client.io.Format;
import com.marklogic.client.io.JacksonParserHandle;
import com.marklogic.client.io.OutputStreamSender;
import com.marklogic.client.io.JacksonHandle;
import com.marklogic.client.io.StringHandle;
import com.marklogic.client.io.marker.AbstractReadHandle;
import com.marklogic.client.io.marker.AbstractWriteHandle;
import com.marklogic.client.io.marker.ContentHandle;
import com.marklogic.client.io.marker.DocumentMetadataReadHandle;
import com.marklogic.client.io.marker.DocumentMetadataWriteHandle;
import com.marklogic.client.io.marker.DocumentPatchHandle;
import com.marklogic.client.io.marker.SearchReadHandle;
import com.marklogic.client.io.marker.StructureWriteHandle;
import com.marklogic.client.query.DeleteQueryDefinition;
import com.marklogic.client.query.ElementLocator;
import com.marklogic.client.query.KeyLocator;
import com.marklogic.client.query.KeyValueQueryDefinition;
import com.marklogic.client.query.QueryDefinition;
import com.marklogic.client.query.QueryManager.QueryView;
import com.marklogic.client.query.RawCombinedQueryDefinition;
import com.marklogic.client.query.RawQueryByExampleDefinition;
import com.marklogic.client.query.RawQueryDefinition;
import com.marklogic.client.query.RawStructuredQueryDefinition;
import com.marklogic.client.query.StringQueryDefinition;
import com.marklogic.client.query.StructuredQueryDefinition;
import com.marklogic.client.query.SuggestDefinition;
import com.marklogic.client.query.ValueLocator;
import com.marklogic.client.query.ValueQueryDefinition;
import com.marklogic.client.query.ValuesDefinition;
import com.marklogic.client.query.ValuesListDefinition;
import com.marklogic.client.semantics.Capability;
import com.marklogic.client.semantics.GraphManager;
import com.marklogic.client.semantics.GraphPermissions;
import com.marklogic.client.semantics.SPARQLBinding;
import com.marklogic.client.semantics.SPARQLBindings;
import com.marklogic.client.semantics.SPARQLQueryDefinition;
import com.marklogic.client.semantics.SPARQLRuleset;
import com.marklogic.client.util.EditableNamespaceContext;
import com.marklogic.client.util.RequestLogger;
import com.marklogic.client.util.RequestParameters;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.api.client.filter.HTTPDigestAuthFilter;
import com.sun.jersey.api.uri.UriComponent;
import com.sun.jersey.client.apache4.ApacheHttpClient4;
import com.sun.jersey.client.apache4.config.ApacheHttpClient4Config;
import com.sun.jersey.client.apache4.config.DefaultApacheHttpClient4Config;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import com.sun.jersey.core.header.ContentDisposition;
import com.sun.jersey.multipart.BodyPart;
import com.sun.jersey.multipart.Boundary;
import com.sun.jersey.multipart.MultiPart;
import com.sun.jersey.multipart.MultiPartMediaTypes;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class JerseyServices implements RESTServices {
static final private Logger logger = LoggerFactory
.getLogger(JerseyServices.class);
static final String ERROR_NS = "http://marklogic.com/xdmp/error";
static final private String DOCUMENT_URI_PREFIX = "/documents?uri=";
static final private int DELAY_FLOOR = 125;
static final private int DELAY_CEILING = 2000;
static final private int DELAY_MULTIPLIER = 20;
static final private int DEFAULT_MAX_DELAY = 120000;
static final private int DEFAULT_MIN_RETRY = 8;
static final private String MAX_DELAY_PROP = "com.marklogic.client.maximumRetrySeconds";
static final private String MIN_RETRY_PROP = "com.marklogic.client.minimumRetries";
static protected class HostnameVerifierAdapter extends AbstractVerifier {
private SSLHostnameVerifier verifier;
protected HostnameVerifierAdapter(SSLHostnameVerifier verifier) {
super();
this.verifier = verifier;
}
@Override
public void verify(String hostname, String[] cns, String[] subjectAlts)
throws SSLException {
verifier.verify(hostname, cns, subjectAlts);
}
}
private DatabaseClient databaseClient;
private String database = null;
private ApacheHttpClient4 client;
private WebResource connection;
private boolean released = false;
private Random randRetry = new Random();
private int maxDelay = DEFAULT_MAX_DELAY;
private int minRetry = DEFAULT_MIN_RETRY;
private boolean checkFirstRequest = false;
static protected class ThreadState {
boolean isFirstRequest;
ThreadState(boolean value) {
isFirstRequest = value;
}
}
// workaround: Jersey keeps the DIGEST nonce in a thread-local variable
private final ThreadLocal threadState = new ThreadLocal() {
@Override
protected ThreadState initialValue() {
return new ThreadState(checkFirstRequest);
}
};
public JerseyServices() {
}
private FailedRequest extractErrorFields(ClientResponse response) {
if ( response == null ) return null;
InputStream is = response.getEntityInputStream();
try {
FailedRequest handler = FailedRequest.getFailedRequest(
response.getStatus(), response.getType(), is);
return handler;
} catch (RuntimeException e) {
throw (e);
} finally {
response.close();
}
}
@Override
public void connect(String host, int port, String database, String user, String password,
Authentication authenType, SSLContext context,
SSLHostnameVerifier verifier) {
X509HostnameVerifier x509Verifier = null;
if (verifier == null) {
if (context != null)
x509Verifier = SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
}
else if (verifier == SSLHostnameVerifier.ANY)
x509Verifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
else if (verifier == SSLHostnameVerifier.COMMON)
x509Verifier = SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
else if (verifier == SSLHostnameVerifier.STRICT)
x509Verifier = SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
else if (context != null)
x509Verifier = new HostnameVerifierAdapter(verifier);
else
throw new IllegalArgumentException(
"Null SSLContent but non-null SSLHostnameVerifier for client");
connect(host, port, database, user, password, authenType, context, x509Verifier);
}
private void connect(String host, int port, String database, String user, String password,
Authentication authenType, SSLContext context,
X509HostnameVerifier verifier) {
if (logger.isDebugEnabled())
logger.debug("Connecting to {} at {} as {}", new Object[] { host,
port, user });
if (host == null)
throw new IllegalArgumentException("No host provided");
if (authenType == null) {
if (context != null) {
authenType = Authentication.BASIC;
}
}
if (authenType != null) {
if (user == null)
throw new IllegalArgumentException("No user provided");
if (password == null)
throw new IllegalArgumentException("No password provided");
}
if (connection != null)
connection = null;
if (client != null) {
client.destroy();
client = null;
}
this.database = database;
String baseUri = ((context == null) ? "http" : "https") + "://" + host
+ ":" + port + "/v1/";
Properties props = System.getProperties();
if (props.containsKey(MAX_DELAY_PROP)) {
String maxDelayStr = props.getProperty(MAX_DELAY_PROP);
if (maxDelayStr != null && maxDelayStr.length() > 0) {
int max = Integer.parseInt(maxDelayStr);
if (max > 0) {
maxDelay = max * 1000;
}
}
}
if (props.containsKey(MIN_RETRY_PROP)) {
String minRetryStr = props.getProperty(MIN_RETRY_PROP);
if (minRetryStr != null && minRetryStr.length() > 0) {
int min = Integer.parseInt(minRetryStr);
if (min > 0) {
minRetry = min;
}
}
}
// TODO: integrated control of HTTP Client and Jersey Client logging
if (!props.containsKey("org.apache.commons.logging.Log")) {
System.setProperty("org.apache.commons.logging.Log",
"org.apache.commons.logging.impl.SimpleLog");
}
if (!props.containsKey("org.apache.commons.logging.simplelog.log.org.apache.http")) {
System.setProperty(
"org.apache.commons.logging.simplelog.log.org.apache.http",
"warn");
}
if (!props.containsKey("org.apache.commons.logging.simplelog.log.org.apache.http.wire")) {
System.setProperty(
"org.apache.commons.logging.simplelog.log.org.apache.http.wire",
"warn");
}
Scheme scheme = null;
if (context == null) {
SchemeSocketFactory socketFactory = PlainSocketFactory
.getSocketFactory();
scheme = new Scheme("http", port, socketFactory);
} else {
SSLSocketFactory socketFactory = new SSLSocketFactory(context,
verifier);
scheme = new Scheme("https", port, socketFactory);
}
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(scheme);
int maxRouteConnections = 100;
int maxTotalConnections = 2 * maxRouteConnections;
/*
* 4.2 PoolingClientConnectionManager connMgr = new
* PoolingClientConnectionManager(schemeRegistry);
* connMgr.setMaxTotal(maxTotalConnections);
* connMgr.setDefaultMaxPerRoute(maxRouteConnections);
* connMgr.setMaxPerRoute( new HttpRoute(new HttpHost(baseUri)),
* maxRouteConnections);
*/
// start 4.1
ThreadSafeClientConnManager connMgr = new ThreadSafeClientConnManager(
schemeRegistry);
connMgr.setMaxTotal(maxTotalConnections);
connMgr.setDefaultMaxPerRoute(maxRouteConnections);
connMgr.setMaxForRoute(new HttpRoute(new HttpHost(baseUri)),
maxRouteConnections);
// end 4.1
// CredentialsProvider credentialsProvider = new
// BasicCredentialsProvider();
// credentialsProvider.setCredentials(new AuthScope(host, port),
// new UsernamePasswordCredentials(user, password));
HttpParams httpParams = new BasicHttpParams();
if (authenType != null) {
List authpref = new ArrayList();
if (authenType == Authentication.BASIC)
authpref.add(AuthPolicy.BASIC);
else if (authenType == Authentication.DIGEST)
authpref.add(AuthPolicy.DIGEST);
else
throw new MarkLogicInternalException(
"Internal error - unknown authentication type: "
+ authenType.name());
httpParams.setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);
}
httpParams.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false);
HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
// HttpConnectionParams.setStaleCheckingEnabled(httpParams, false);
// long-term alternative to isFirstRequest alive
// HttpProtocolParams.setUseExpectContinue(httpParams, false);
// httpParams.setIntParameter(CoreProtocolPNames.WAIT_FOR_CONTINUE, 1000);
DefaultApacheHttpClient4Config config = new DefaultApacheHttpClient4Config();
Map configProps = config.getProperties();
configProps
.put(ApacheHttpClient4Config.PROPERTY_PREEMPTIVE_BASIC_AUTHENTICATION,
false);
configProps.put(ApacheHttpClient4Config.PROPERTY_DISABLE_COOKIES, true);
configProps.put(ApacheHttpClient4Config.PROPERTY_CONNECTION_MANAGER,
connMgr);
// ignored?
configProps.put(ApacheHttpClient4Config.PROPERTY_FOLLOW_REDIRECTS,
false);
// configProps.put(ApacheHttpClient4Config.PROPERTY_CREDENTIALS_PROVIDER,
// credentialsProvider);
configProps.put(ApacheHttpClient4Config.PROPERTY_HTTP_PARAMS,
httpParams);
// switches from buffered to streamed in Jersey Client
configProps.put(ApacheHttpClient4Config.PROPERTY_CHUNKED_ENCODING_SIZE,
32 * 1024);
client = ApacheHttpClient4.create(config);
// System.setProperty("javax.net.debug", "all"); // all or ssl
if (authenType == null) {
checkFirstRequest = false;
} else if (authenType == Authentication.BASIC) {
checkFirstRequest = false;
client.addFilter(new HTTPBasicAuthFilter(user, password));
} else if (authenType == Authentication.DIGEST) {
checkFirstRequest = true;
// workaround for JerseyClient bug 1445
client.addFilter(new DigestChallengeFilter());
client.addFilter(new HTTPDigestAuthFilter(user, password));
} else {
throw new MarkLogicInternalException(
"Internal error - unknown authentication type: "
+ authenType.name());
}
// client.addFilter(new LoggingFilter(System.err));
connection = client.resource(baseUri);
}
@Override
public DatabaseClient getDatabaseClient() {
return databaseClient;
}
@Override
public void setDatabaseClient(DatabaseClient client) {
this.databaseClient = client;
}
private WebResource getConnection() {
if ( connection != null ) return connection;
else if ( released ) throw new IllegalStateException(
"You cannot use this connected object anymore--connection has already been released");
else throw new MarkLogicInternalException("Cannot proceed--connection is null for unknown reason");
}
@Override
public void release() {
released = true;
if (databaseClient != null) {
databaseClient = null;
}
if (client == null)
return;
if (logger.isDebugEnabled())
logger.debug("Releasing connection");
connection = null;
client.destroy();
client = null;
}
private boolean isFirstRequest() {
return threadState.get().isFirstRequest;
}
private void setFirstRequest(boolean value) {
threadState.get().isFirstRequest = value;
}
private void checkFirstRequest() {
if (checkFirstRequest)
setFirstRequest(true);
}
private int makeFirstRequest(int retry) {
ClientResponse response = getConnection().path("ping").head();
int statusCode = response.getClientResponseStatus().getStatusCode();
if (statusCode != ClientResponse.Status.SERVICE_UNAVAILABLE.getStatusCode()) {
response.close();
return 0;
}
MultivaluedMap responseHeaders = response.getHeaders();
response.close();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
return Math.max(retryAfter, calculateDelay(randRetry, retry));
}
@Override
public TemporalDescriptor deleteDocument(RequestLogger reqlog, DocumentDescriptor desc,
Transaction transaction, Set categories, RequestParameters extraParams)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
String uri = desc.getUri();
if (uri == null)
throw new IllegalArgumentException(
"Document delete for document identifier without uri");
if (logger.isDebugEnabled())
logger.debug("Deleting {} in transaction {}", uri, getTransactionId(transaction));
WebResource webResource = makeDocumentResource(makeDocumentParams(uri,
categories, transaction, extraParams));
WebResource.Builder builder = addVersionHeader(desc,
webResource.getRequestBuilder(), "If-Match");
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
MultivaluedMap responseHeaders = null;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.delete(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.NOT_FOUND) {
response.close();
throw new ResourceNotFoundException(
"Could not delete non-existent document");
}
if (status == ClientResponse.Status.FORBIDDEN) {
FailedRequest failure = extractErrorFields(response);
if (failure.getMessageCode().equals("RESTAPI-CONTENTNOVERSION"))
throw new FailedRequestException(
"Content version required to delete document", failure);
throw new ForbiddenUserException(
"User is not allowed to delete documents", failure);
}
if (status == ClientResponse.Status.PRECONDITION_FAILED) {
FailedRequest failure = extractErrorFields(response);
if (failure.getMessageCode().equals("RESTAPI-CONTENTWRONGVERSION"))
throw new FailedRequestException(
"Content version must match to delete document",
failure);
else if (failure.getMessageCode().equals("RESTAPI-EMPTYBODY"))
throw new FailedRequestException(
"Empty request body sent to server", failure);
throw new FailedRequestException("Precondition Failed", failure);
}
if (status != ClientResponse.Status.NO_CONTENT)
throw new FailedRequestException("delete failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
responseHeaders = response.getHeaders();
TemporalDescriptor temporalDesc = updateTemporalSystemTime(desc, responseHeaders);
response.close();
logRequest(reqlog, "deleted %s document", uri);
return temporalDesc;
}
@Override
public boolean getDocument(RequestLogger reqlog, DocumentDescriptor desc,
Transaction transaction, Set categories,
RequestParameters extraParams,
DocumentMetadataReadHandle metadataHandle,
AbstractReadHandle contentHandle) throws ResourceNotFoundException,
ForbiddenUserException, FailedRequestException {
HandleImplementation metadataBase = HandleAccessor.checkHandle(
metadataHandle, "metadata");
HandleImplementation contentBase = HandleAccessor.checkHandle(
contentHandle, "content");
String metadataFormat = null;
String metadataMimetype = null;
if (metadataBase != null) {
metadataFormat = metadataBase.getFormat().toString().toLowerCase();
metadataMimetype = metadataBase.getMimetype();
}
String contentMimetype = null;
if (contentBase != null) {
contentMimetype = contentBase.getMimetype();
}
if (metadataBase != null && contentBase != null) {
return getDocumentImpl(reqlog, desc, transaction, categories,
extraParams, metadataFormat, metadataHandle, contentHandle);
} else if (metadataBase != null) {
return getDocumentImpl(reqlog, desc, transaction, categories,
extraParams, metadataMimetype, metadataHandle);
} else if (contentBase != null) {
return getDocumentImpl(reqlog, desc, transaction, null,
extraParams, contentMimetype, contentHandle);
}
return false;
}
private boolean getDocumentImpl(RequestLogger reqlog,
DocumentDescriptor desc, Transaction transaction,
Set categories, RequestParameters extraParams,
String mimetype, AbstractReadHandle handle)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
String uri = desc.getUri();
if (uri == null)
throw new IllegalArgumentException(
"Document read for document identifier without uri");
if (logger.isDebugEnabled())
logger.debug("Getting {} in transaction {}", uri, getTransactionId(transaction));
WebResource webResource = makeDocumentResource(
makeDocumentParams(uri, categories, transaction, extraParams));
WebResource.Builder builder = webResource.accept(mimetype);
addTransactionScopedCookies(builder, webResource, transaction);
if (extraParams != null && extraParams.containsKey("range"))
builder = builder.header("range", extraParams.get("range").get(0));
builder = addVersionHeader(desc, builder, "If-None-Match");
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.get(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.NOT_FOUND)
throw new ResourceNotFoundException(
"Could not read non-existent document",
extractErrorFields(response));
if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException(
"User is not allowed to read documents",
extractErrorFields(response));
if (status == ClientResponse.Status.NOT_MODIFIED) {
response.close();
return false;
}
if (status != ClientResponse.Status.OK
&& status != ClientResponse.Status.PARTIAL_CONTENT)
throw new FailedRequestException("read failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
logRequest(
reqlog,
"read %s document from %s transaction with %s mime type and %s metadata categories",
uri, (transaction != null) ? transaction.getTransactionId() : "no",
(mimetype != null) ? mimetype : "no",
stringJoin(categories, ", ", "no"));
HandleImplementation handleBase = HandleAccessor.as(handle);
MultivaluedMap responseHeaders = response.getHeaders();
if (isExternalDescriptor(desc)) {
updateVersion(desc, responseHeaders);
updateDescriptor(desc, responseHeaders);
copyDescriptor(desc, handleBase);
} else {
updateDescriptor(handleBase, responseHeaders);
}
Class as = handleBase.receiveAs();
Object entity = response.hasEntity() ? response.getEntity(as) : null;
if (entity == null ||
(!InputStream.class.isAssignableFrom(as) && !Reader.class.isAssignableFrom(as)))
response.close();
handleBase.receiveContent((reqlog != null) ? reqlog.copyContent(entity)
: entity);
return true;
}
@Override
public DocumentPage getBulkDocuments(RequestLogger reqlog,
Transaction transaction, Set categories,
Format format, RequestParameters extraParams, boolean withContent, String... uris)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException {
boolean hasMetadata = categories != null && categories.size() > 0;
JerseyResultIterator iterator =
getBulkDocumentsImpl(reqlog, transaction, categories, format, extraParams, withContent, uris);
return new JerseyDocumentPage(iterator, withContent, hasMetadata);
}
@Override
public DocumentPage getBulkDocuments(RequestLogger reqlog,
QueryDefinition querydef,
long start, long pageLength,
Transaction transaction,
SearchReadHandle searchHandle, QueryView view,
Set categories, Format format, RequestParameters extraParams)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException {
boolean hasMetadata = categories != null && categories.size() > 0;
boolean hasContent = true;
JerseyResultIterator iterator =
getBulkDocumentsImpl(reqlog, querydef, start, pageLength, transaction,
searchHandle, view, categories, format, extraParams);
return new JerseyDocumentPage(iterator, hasContent, hasMetadata);
}
private class JerseyDocumentPage extends BasicPage implements DocumentPage, Iterator {
private JerseyResultIterator iterator;
private Iterator docRecordIterator;
private boolean hasMetadata;
private boolean hasContent;
JerseyDocumentPage(JerseyResultIterator iterator, boolean hasContent, boolean hasMetadata) {
super(
new ArrayList().iterator(),
iterator != null ? iterator.getStart() : 1,
iterator != null ? iterator.getPageSize() : 0,
iterator != null ? iterator.getTotalSize() : 0
);
this.iterator = iterator;
this.hasContent = hasContent;
this.hasMetadata = hasMetadata;
if ( iterator == null ) {
setSize(0);
} else if ( hasContent && hasMetadata ) {
setSize(iterator.getSize() / 2);
} else {
setSize(iterator.getSize());
}
}
@Override
public Iterator iterator() {
return this;
}
@Override
public boolean hasNext() {
if ( iterator == null ) return false;
return iterator.hasNext();
}
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public DocumentRecord next() {
if ( iterator == null ) throw new NoSuchElementException("No documents available");
JerseyResult result = iterator.next();
DocumentRecord record;
if ( hasContent && hasMetadata ) {
JerseyResult metadata = result;
JerseyResult content = iterator.next();
record = new JerseyDocumentRecord(content, metadata);
} else if ( hasContent ) {
JerseyResult content = result;
record = new JerseyDocumentRecord(content);
} else if ( hasMetadata ) {
JerseyResult metadata = result;
record = new JerseyDocumentRecord(null, metadata);
} else {
throw new IllegalStateException("Should never have neither content nor metadata");
}
return record;
}
public T nextContent(T contentHandle) {
return next().getContent(contentHandle);
}
public void close() {
if ( iterator != null ) iterator.close();
}
}
private JerseyResultIterator getBulkDocumentsImpl(RequestLogger reqlog,
Transaction transaction, Set categories,
Format format, RequestParameters extraParams, boolean withContent, String... uris)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException {
String path = "documents";
RequestParameters params = new RequestParameters();
if ( extraParams != null ) params.putAll(extraParams);
addCategoryParams(categories, params, withContent);
if (format != null) params.add("format", format.toString().toLowerCase());
for (String uri: uris) {
params.add("uri", uri);
}
JerseyResultIterator iterator = getIteratedResourceImpl(DefaultJerseyResultIterator.class,
reqlog, path, transaction, params, MultiPartMediaTypes.MULTIPART_MIXED);
if ( iterator != null ) {
if ( iterator.getStart() == -1 ) iterator.setStart(1);
if ( iterator.getSize() != -1 ) {
if ( iterator.getPageSize() == -1 ) iterator.setPageSize(iterator.getSize());
if ( iterator.getTotalSize() == -1 ) iterator.setTotalSize(iterator.getSize());
}
}
return iterator;
}
private JerseyResultIterator getBulkDocumentsImpl(RequestLogger reqlog,
QueryDefinition querydef, long start, long pageLength,
Transaction transaction, SearchReadHandle searchHandle, QueryView view,
Set categories, Format format, RequestParameters extraParams)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException {
MultivaluedMap params = new MultivaluedMapImpl();
if ( extraParams != null ) params.putAll(extraParams);
boolean withContent = true;
addCategoryParams(categories, params, withContent);
if (searchHandle != null && view != null) params.add("view", view.toString().toLowerCase());
if (start > 1) params.add("start", Long.toString(start));
if (pageLength >= 0) params.add("pageLength", Long.toString(pageLength));
if (format != null) params.add("format", format.toString().toLowerCase());
if ( format == null && searchHandle != null ) {
HandleImplementation handleBase = HandleAccessor.as(searchHandle);
if ( Format.XML == handleBase.getFormat() ) {
params.add("format", "xml");
} else if ( Format.JSON == handleBase.getFormat() ) {
params.add("format", "json");
}
}
JerseySearchRequest request =
generateSearchRequest(reqlog, querydef, MultiPartMediaTypes.MULTIPART_MIXED, transaction, params);
ClientResponse response = request.getResponse();
if ( response == null ) return null;
MultiPart entity = null;
if ( searchHandle != null ) {
if ( response.hasEntity() ) {
entity = response.getEntity(MultiPart.class);
if ( entity != null ) {
List partList = entity.getBodyParts();
if ( partList != null && partList.size() > 0 ) {
BodyPart searchResponsePart = partList.get(0);
HandleImplementation handleBase = HandleAccessor.as(searchHandle);
handleBase.receiveContent(
searchResponsePart.getEntityAs(handleBase.receiveAs())
);
partList = partList.subList(1, partList.size());
}
Closeable closeable = new MultipartCloseable(response, entity);
return makeResults(JerseyServiceResultIterator.class, reqlog, "read", "resource",
partList, response, closeable);
}
}
}
return makeResults(JerseyServiceResultIterator.class, reqlog, "read", "resource", response);
}
private boolean getDocumentImpl(RequestLogger reqlog,
DocumentDescriptor desc, Transaction transaction,
Set categories, RequestParameters extraParams,
String metadataFormat, DocumentMetadataReadHandle metadataHandle,
AbstractReadHandle contentHandle) throws ResourceNotFoundException,
ForbiddenUserException, FailedRequestException {
String uri = desc.getUri();
if (uri == null)
throw new IllegalArgumentException(
"Document read for document identifier without uri");
assert metadataHandle != null : "metadataHandle is null";
assert contentHandle != null : "contentHandle is null";
if (logger.isDebugEnabled())
logger.debug("Getting multipart for {} in transaction {}", uri,
getTransactionId(transaction));
MultivaluedMap docParams = makeDocumentParams(uri,
categories, transaction, extraParams, true);
docParams.add("format", metadataFormat);
WebResource webResource = makeDocumentResource(docParams);
WebResource.Builder builder = webResource.getRequestBuilder();
addTransactionScopedCookies(builder, webResource, transaction);
builder = addVersionHeader(desc, builder, "If-None-Match");
MediaType multipartType = Boundary.addBoundary(MultiPartMediaTypes.MULTIPART_MIXED_TYPE);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.accept(multipartType).get(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.NOT_FOUND)
throw new ResourceNotFoundException(
"Could not read non-existent document",
extractErrorFields(response));
if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException(
"User is not allowed to read documents",
extractErrorFields(response));
if (status == ClientResponse.Status.NOT_MODIFIED) {
response.close();
return false;
}
if (status != ClientResponse.Status.OK)
throw new FailedRequestException("read failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
logRequest(
reqlog,
"read %s document from %s transaction with %s metadata categories and content",
uri, (transaction != null) ? transaction.getTransactionId() : "no",
stringJoin(categories, ", ", "no"));
MultiPart entity = response.hasEntity() ?
response.getEntity(MultiPart.class) : null;
if (entity == null)
return false;
List partList = entity.getBodyParts();
if (partList == null)
return false;
int partCount = partList.size();
if (partCount == 0)
return false;
if (partCount != 2)
throw new FailedRequestException("read expected 2 parts but got "
+ partCount + " parts");
HandleImplementation metadataBase = HandleAccessor.as(metadataHandle);
HandleImplementation contentBase = HandleAccessor.as(contentHandle);
BodyPart contentPart = partList.get(1);
MultivaluedMap responseHeaders = response.getHeaders();
MultivaluedMap contentHeaders = contentPart
.getHeaders();
if (isExternalDescriptor(desc)) {
updateVersion(desc, responseHeaders);
updateFormat(desc, responseHeaders);
updateMimetype(desc, contentHeaders);
updateLength(desc, contentHeaders);
copyDescriptor(desc, contentBase);
} else {
updateFormat(contentBase, responseHeaders);
updateMimetype(contentBase, contentHeaders);
updateLength(contentBase, contentHeaders);
}
metadataBase.receiveContent(partList.get(0).getEntityAs(
metadataBase.receiveAs()));
Object contentEntity = contentPart.getEntityAs(contentBase.receiveAs());
contentBase.receiveContent((reqlog != null) ? reqlog
.copyContent(contentEntity) : contentEntity);
try { entity.close(); } catch (IOException e) {}
response.close();
return true;
}
@Override
public DocumentDescriptor head(RequestLogger reqlog, String uri,
Transaction transaction) throws ForbiddenUserException,
FailedRequestException {
ClientResponse response = headImpl(reqlog, uri, transaction, makeDocumentResource(makeDocumentParams(uri,
null, transaction, null)));
// 404
if (response == null) return null;
MultivaluedMap responseHeaders = response.getHeaders();
response.close();
logRequest(reqlog, "checked %s document from %s transaction", uri,
(transaction != null) ? transaction.getTransactionId() : "no");
DocumentDescriptorImpl desc = new DocumentDescriptorImpl(uri, false);
updateVersion(desc, responseHeaders);
updateDescriptor(desc, responseHeaders);
return desc;
}
@Override
public boolean exists(String uri) throws ForbiddenUserException,
FailedRequestException {
return headImpl(null, uri, null, getConnection().path(uri)) == null ? false : true;
}
public ClientResponse headImpl(RequestLogger reqlog, String uri,
Transaction transaction, WebResource webResource) {
if (uri == null)
throw new IllegalArgumentException(
"Existence check for document identifier without uri");
if (logger.isDebugEnabled())
logger.debug("Requesting head for {} in transaction {}", uri,
getTransactionId(transaction));
WebResource.Builder builder = webResource.getRequestBuilder();
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.head();
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status != ClientResponse.Status.OK) {
if (status == ClientResponse.Status.NOT_FOUND) {
response.close();
return null;
} else if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException(
"User is not allowed to check the existence of documents",
extractErrorFields(response));
else
throw new FailedRequestException(
"Document existence check failed: "
+ status.getReasonPhrase(),
extractErrorFields(response));
}
return response;
}
@Override
public TemporalDescriptor putDocument(RequestLogger reqlog, DocumentDescriptor desc,
Transaction transaction, Set categories,
RequestParameters extraParams,
DocumentMetadataWriteHandle metadataHandle,
AbstractWriteHandle contentHandle)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
if (desc.getUri() == null)
throw new IllegalArgumentException(
"Document write for document identifier without uri");
HandleImplementation metadataBase = HandleAccessor.checkHandle(
metadataHandle, "metadata");
HandleImplementation contentBase = HandleAccessor.checkHandle(
contentHandle, "content");
String metadataMimetype = null;
if (metadataBase != null) {
metadataMimetype = metadataBase.getMimetype();
}
Format descFormat = desc.getFormat();
String contentMimetype = (descFormat != null && descFormat != Format.UNKNOWN) ? desc
.getMimetype() : null;
if (contentMimetype == null && contentBase != null) {
Format contentFormat = contentBase.getFormat();
if (descFormat != null && descFormat != contentFormat) {
contentMimetype = descFormat.getDefaultMimetype();
} else if (contentFormat != null && contentFormat != Format.UNKNOWN) {
contentMimetype = contentBase.getMimetype();
}
}
if (metadataBase != null && contentBase != null) {
return putPostDocumentImpl(reqlog, "put", desc, transaction, categories,
extraParams, metadataMimetype, metadataHandle,
contentMimetype, contentHandle);
} else if (metadataBase != null) {
return putPostDocumentImpl(reqlog, "put", desc, transaction, categories, false,
extraParams, metadataMimetype, metadataHandle);
} else if (contentBase != null) {
return putPostDocumentImpl(reqlog, "put", desc, transaction, null, true,
extraParams, contentMimetype, contentHandle);
}
throw new IllegalArgumentException("Either metadataHandle or contentHandle must not be null");
}
@Override
public DocumentDescriptorImpl postDocument(RequestLogger reqlog, DocumentUriTemplate template,
Transaction transaction, Set categories, RequestParameters extraParams,
DocumentMetadataWriteHandle metadataHandle, AbstractWriteHandle contentHandle)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException {
DocumentDescriptorImpl desc = new DocumentDescriptorImpl(false);
HandleImplementation metadataBase = HandleAccessor.checkHandle(
metadataHandle, "metadata");
HandleImplementation contentBase = HandleAccessor.checkHandle(
contentHandle, "content");
String metadataMimetype = null;
if (metadataBase != null) {
metadataMimetype = metadataBase.getMimetype();
}
Format templateFormat = template.getFormat();
String contentMimetype = (templateFormat != null && templateFormat != Format.UNKNOWN) ?
template.getMimetype() : null;
if (contentMimetype == null && contentBase != null) {
Format contentFormat = contentBase.getFormat();
if (templateFormat != null && templateFormat != contentFormat) {
contentMimetype = templateFormat.getDefaultMimetype();
desc.setFormat(templateFormat);
} else if (contentFormat != null && contentFormat != Format.UNKNOWN) {
contentMimetype = contentBase.getMimetype();
desc.setFormat(contentFormat);
}
}
desc.setMimetype(contentMimetype);
if (extraParams == null)
extraParams = new RequestParameters();
String extension = template.getExtension();
if (extension != null)
extraParams.add("extension", extension);
String directory = template.getDirectory();
if (directory != null)
extraParams.add("directory", directory);
if (metadataBase != null && contentBase != null) {
putPostDocumentImpl(reqlog, "post", desc, transaction, categories, extraParams,
metadataMimetype, metadataHandle, contentMimetype, contentHandle);
} else if (contentBase != null) {
putPostDocumentImpl(reqlog, "post", desc, transaction, null, true, extraParams,
contentMimetype, contentHandle);
}
return desc;
}
private TemporalDescriptor putPostDocumentImpl(RequestLogger reqlog, String method, DocumentDescriptor desc,
Transaction transaction, Set categories, boolean isOnContent, RequestParameters extraParams,
String mimetype, AbstractWriteHandle handle)
throws ResourceNotFoundException, ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
String uri = desc.getUri();
HandleImplementation handleBase = HandleAccessor.as(handle);
if (logger.isDebugEnabled())
logger.debug("Sending {} document in transaction {}",
(uri != null) ? uri : "new", getTransactionId(transaction));
logRequest(
reqlog,
"writing %s document from %s transaction with %s mime type and %s metadata categories",
(uri != null) ? uri : "new",
(transaction != null) ? transaction.getTransactionId() : "no",
(mimetype != null) ? mimetype : "no",
stringJoin(categories, ", ", "no"));
WebResource webResource = makeDocumentResource(
makeDocumentParams(
uri, categories, transaction, extraParams, isOnContent
));
WebResource.Builder builder = webResource.type(
(mimetype != null) ? mimetype : MediaType.WILDCARD);
addTransactionScopedCookies(builder, webResource, transaction);
if (uri != null) {
builder = addVersionHeader(desc, builder, "If-Match");
}
if ("patch".equals(method)) {
builder = builder.header("X-HTTP-Method-Override", "PATCH");
method = "post";
}
boolean isResendable = handleBase.isResendable();
ClientResponse response = null;
ClientResponse.Status status = null;
MultivaluedMap responseHeaders = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
Object value = handleBase.sendContent();
if (value == null)
throw new IllegalArgumentException(
"Document write with null value for " +
((uri != null) ? uri : "new document"));
if (isFirstRequest() && !isResendable && isStreaming(value)) {
nextDelay = makeFirstRequest(retry);
if (nextDelay != 0)
continue;
}
if (value instanceof OutputStreamSender) {
StreamingOutput sentStream =
new StreamingOutputImpl((OutputStreamSender) value, reqlog);
response =
("put".equals(method)) ?
builder.put(ClientResponse.class, sentStream) :
builder.post(ClientResponse.class, sentStream);
} else {
Object sentObj = (reqlog != null) ?
reqlog.copyContent(value) : value;
response =
("put".equals(method)) ?
builder.put(ClientResponse.class, sentObj) :
builder.post(ClientResponse.class, sentObj);
}
status = response.getClientResponseStatus();
responseHeaders = response.getHeaders();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
response.close();
if (!isResendable) {
checkFirstRequest();
throw new ResourceNotResendableException(
"Cannot retry request for " +
((uri != null) ? uri : "new document"));
}
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.NOT_FOUND)
throw new ResourceNotFoundException(
"Could not write non-existent document",
extractErrorFields(response));
if (status == ClientResponse.Status.FORBIDDEN) {
FailedRequest failure = extractErrorFields(response);
if (failure.getMessageCode().equals("RESTAPI-CONTENTNOVERSION"))
throw new FailedRequestException(
"Content version required to write document", failure);
throw new ForbiddenUserException(
"User is not allowed to write documents", failure);
}
if (status == ClientResponse.Status.PRECONDITION_FAILED) {
FailedRequest failure = extractErrorFields(response);
if (failure.getMessageCode().equals("RESTAPI-CONTENTWRONGVERSION"))
throw new FailedRequestException(
"Content version must match to write document", failure);
else if (failure.getMessageCode().equals("RESTAPI-EMPTYBODY"))
throw new FailedRequestException(
"Empty request body sent to server", failure);
throw new FailedRequestException("Precondition Failed", failure);
}
if (status == null) {
throw new FailedRequestException("write failed: Unknown Reason", extractErrorFields(response));
}
if (status != ClientResponse.Status.CREATED
&& status != ClientResponse.Status.NO_CONTENT) {
throw new FailedRequestException("write failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
if (uri == null) {
String location = responseHeaders.getFirst("Location");
if (location != null) {
int offset = location.indexOf(DOCUMENT_URI_PREFIX);
if (offset == -1)
throw new MarkLogicInternalException(
"document create produced invalid location: " + location);
uri = location.substring(offset + DOCUMENT_URI_PREFIX.length());
if (uri == null)
throw new MarkLogicInternalException(
"document create produced location without uri: " + location);
desc.setUri(uri);
updateVersion(desc, responseHeaders);
updateDescriptor(desc, responseHeaders);
}
}
TemporalDescriptor temporalDesc = updateTemporalSystemTime(desc, responseHeaders);
response.close();
return temporalDesc;
}
private TemporalDescriptor putPostDocumentImpl(RequestLogger reqlog, String method, DocumentDescriptor desc,
Transaction transaction, Set categories, RequestParameters extraParams,
String metadataMimetype, DocumentMetadataWriteHandle metadataHandle, String contentMimetype,
AbstractWriteHandle contentHandle)
throws ResourceNotFoundException, ResourceNotResendableException,
ForbiddenUserException, FailedRequestException {
String uri = desc.getUri();
if (logger.isDebugEnabled())
logger.debug("Sending {} multipart document in transaction {}",
(uri != null) ? uri : "new", getTransactionId(transaction));
logRequest(
reqlog,
"writing %s document from %s transaction with %s metadata categories and content",
(uri != null) ? uri : "new",
(transaction != null) ? transaction.getTransactionId() : "no",
stringJoin(categories, ", ", "no"));
MultivaluedMap docParams =
makeDocumentParams(uri, categories, transaction, extraParams, true);
WebResource webResource = makeDocumentResource(docParams);
WebResource.Builder builder = webResource.getRequestBuilder();
addTransactionScopedCookies(builder, webResource, transaction);
if (uri != null) {
builder = addVersionHeader(desc, builder, "If-Match");
}
MediaType multipartType = Boundary.addBoundary(MultiPartMediaTypes.MULTIPART_MIXED_TYPE);
ClientResponse response = null;
ClientResponse.Status status = null;
MultivaluedMap responseHeaders = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
MultiPart multiPart = new MultiPart();
boolean hasStreamingPart = addParts(multiPart, reqlog,
new String[] { metadataMimetype, contentMimetype },
new AbstractWriteHandle[] { metadataHandle, contentHandle });
if (isFirstRequest() && hasStreamingPart) {
nextDelay = makeFirstRequest(retry);
if (nextDelay != 0)
continue;
}
// Must set multipart/mixed mime type explicitly on each request
// because Jersey client 1.17 adapter for HttpClient switches
// to application/octet-stream on retry
WebResource.Builder requestBlder = builder.type(multipartType);
response =
("put".equals(method)) ?
requestBlder.put(ClientResponse.class, multiPart) :
requestBlder.post(ClientResponse.class, multiPart);
status = response.getClientResponseStatus();
responseHeaders = response.getHeaders();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
response.close();
if (hasStreamingPart) {
throw new ResourceNotResendableException(
"Cannot retry request for " +
((uri != null) ? uri : "new document"));
}
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.NOT_FOUND) {
response.close();
throw new ResourceNotFoundException(
"Could not write non-existent document");
}
if (status == ClientResponse.Status.FORBIDDEN) {
FailedRequest failure = extractErrorFields(response);
if (failure.getMessageCode().equals("RESTAPI-CONTENTNOVERSION"))
throw new FailedRequestException(
"Content version required to write document", failure);
throw new ForbiddenUserException(
"User is not allowed to write documents", failure);
}
if (status == ClientResponse.Status.PRECONDITION_FAILED) {
FailedRequest failure = extractErrorFields(response);
if (failure.getMessageCode().equals("RESTAPI-CONTENTWRONGVERSION"))
throw new FailedRequestException(
"Content version must match to write document", failure);
else if (failure.getMessageCode().equals("RESTAPI-EMPTYBODY"))
throw new FailedRequestException(
"Empty request body sent to server", failure);
throw new FailedRequestException("Precondition Failed", failure);
}
if (status != ClientResponse.Status.CREATED
&& status != ClientResponse.Status.NO_CONTENT) {
throw new FailedRequestException("write failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
if (uri == null) {
String location = responseHeaders.getFirst("Location");
if (location != null) {
int offset = location.indexOf(DOCUMENT_URI_PREFIX);
if (offset == -1)
throw new MarkLogicInternalException(
"document create produced invalid location: " + location);
uri = location.substring(offset + DOCUMENT_URI_PREFIX.length());
if (uri == null)
throw new MarkLogicInternalException(
"document create produced location without uri: " + location);
desc.setUri(uri);
updateVersion(desc, responseHeaders);
updateDescriptor(desc, responseHeaders);
}
}
TemporalDescriptor temporalDesc = updateTemporalSystemTime(desc, responseHeaders);
response.close();
return temporalDesc;
}
@Override
public void patchDocument(RequestLogger reqlog, DocumentDescriptor desc, Transaction transaction,
Set categories, boolean isOnContent, DocumentPatchHandle patchHandle)
throws ResourceNotFoundException, ResourceNotResendableException,
ForbiddenUserException, FailedRequestException {
HandleImplementation patchBase = HandleAccessor.checkHandle(
patchHandle, "patch");
putPostDocumentImpl(reqlog, "patch", desc, transaction, categories, isOnContent, null,
patchBase.getMimetype(), patchHandle);
}
@Override
public Transaction openTransaction(String name, int timeLimit)
throws ForbiddenUserException, FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Opening transaction");
MultivaluedMap transParams = new MultivaluedMapImpl();
if (name != null || timeLimit > 0) {
if (name != null)
addEncodedParam(transParams, "name", name);
if (timeLimit > 0)
transParams.add("timeLimit", String.valueOf(timeLimit));
}
if ( database != null ) {
addEncodedParam(transParams, "database", database);
}
WebResource resource = (transParams != null) ? getConnection().path(
"transactions").queryParams(transParams) : getConnection()
.path("transactions");
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = resource.post(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException(
"User is not allowed to open transactions",
extractErrorFields(response));
if (status != ClientResponse.Status.SEE_OTHER)
throw new FailedRequestException("transaction open failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
String location = response.getHeaders().getFirst("Location");
String hostId = null;
for ( NewCookie newCookie : response.getCookies() ) {
if ( "HostId".equalsIgnoreCase(newCookie.getName()) ) {
hostId = newCookie.getValue();
break;
}
}
response.close();
if (location == null)
throw new MarkLogicInternalException(
"transaction open failed to provide location");
if (!location.contains("/"))
throw new MarkLogicInternalException(
"transaction open produced invalid location: " + location);
String transactionId = location.substring(location.lastIndexOf("/") + 1);
return new TransactionImpl(this, transactionId, response.getCookies());
}
@Override
public void commitTransaction(Transaction transaction)
throws ForbiddenUserException, FailedRequestException {
completeTransaction(transaction, "commit");
}
@Override
public void rollbackTransaction(Transaction transaction)
throws ForbiddenUserException, FailedRequestException {
completeTransaction(transaction, "rollback");
}
private void completeTransaction(Transaction transaction, String result)
throws ForbiddenUserException, FailedRequestException {
if (result == null)
throw new MarkLogicInternalException(
"transaction completion without operation");
if (transaction == null)
throw new MarkLogicInternalException(
"transaction completion without id: " + result);
if (logger.isDebugEnabled())
logger.debug("Completing transaction {} with {}", transaction.getTransactionId(),
result);
MultivaluedMap transParams = new MultivaluedMapImpl();
transParams.add("result", result);
WebResource webResource = getConnection().path("transactions/" + transaction.getTransactionId())
.queryParams(transParams);
WebResource.Builder builder = webResource.getRequestBuilder();
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.post(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException(
"User is not allowed to complete transaction with "
+ result, extractErrorFields(response));
if (status != ClientResponse.Status.NO_CONTENT)
throw new FailedRequestException("transaction " + result
+ " failed: " + status.getReasonPhrase(),
extractErrorFields(response));
response.close();
}
private void addCategoryParams(Set categories, MultivaluedMap params,
boolean withContent)
{
if (withContent && categories == null || categories.size() == 0) {
params.add("category", "content");
} else {
if (withContent) params.add("category", "content");
if (categories.contains(Metadata.ALL)) {
params.add("category", "metadata");
} else {
for (Metadata category : categories) {
params.add("category", category.name().toLowerCase());
}
}
}
}
private void addCategoryParams(Set categories, RequestParameters params,
boolean withContent)
{
if (withContent && categories == null || categories.size() == 0) {
params.add("category", "content");
} else {
if (withContent) params.add("category", "content");
if (categories.contains(Metadata.ALL)) {
params.add("category", "metadata");
} else {
for (Metadata category : categories) {
params.add("category", category.name().toLowerCase());
}
}
}
}
private MultivaluedMap makeDocumentParams(String uri,
Set categories, Transaction transaction,
RequestParameters extraParams) {
return makeDocumentParams(uri, categories, transaction, extraParams,
false);
}
private MultivaluedMap makeDocumentParams(String uri,
Set categories, Transaction transaction,
RequestParameters extraParams, boolean withContent) {
MultivaluedMap docParams = new MultivaluedMapImpl();
if (extraParams != null && extraParams.size() > 0) {
for (Map.Entry> entry : extraParams.entrySet()) {
String extraKey = entry.getKey();
if (!"range".equalsIgnoreCase(extraKey)) {
addEncodedParam(docParams, extraKey, entry.getValue());
}
}
}
addEncodedParam(docParams, "uri", uri);
if ( database != null ) {
addEncodedParam(docParams, "database", database);
}
if (categories == null || categories.size() == 0) {
docParams.add("category", "content");
} else {
if (withContent)
docParams.add("category", "content");
if (categories.contains(Metadata.ALL)) {
docParams.add("category", "metadata");
} else {
for (Metadata category : categories)
docParams.add("category", category.name().toLowerCase());
}
}
if (transaction != null) {
docParams.add("txid", transaction.getTransactionId());
}
return docParams;
}
private WebResource makeDocumentResource(
MultivaluedMap queryParams) {
return getConnection().path("documents").queryParams(queryParams);
}
private boolean isExternalDescriptor(ContentDescriptor desc) {
return desc != null && desc instanceof DocumentDescriptorImpl
&& !((DocumentDescriptorImpl) desc).isInternal();
}
private void updateDescriptor(ContentDescriptor desc,
MultivaluedMap headers) {
if (desc == null || headers == null)
return;
updateFormat(desc, headers);
updateMimetype(desc, headers);
updateLength(desc, headers);
}
private TemporalDescriptor updateTemporalSystemTime(DocumentDescriptor desc,
MultivaluedMap headers)
{
if (headers == null) return null;
DocumentDescriptorImpl temporalDescriptor;
if ( desc instanceof DocumentDescriptorImpl ) {
temporalDescriptor = (DocumentDescriptorImpl) desc;
} else {
temporalDescriptor = new DocumentDescriptorImpl(desc.getUri(), false);
}
temporalDescriptor.setTemporalSystemTime(getHeaderTemporalSystemTime(headers));
return temporalDescriptor;
}
private String getHeaderTemporalSystemTime(MultivaluedMap headers) {
if (headers.containsKey("x-marklogic-system-time")) {
List values = headers.get("x-marklogic-system-time");
if (values != null) {
return values.get(0);
}
}
return null;
}
private void copyDescriptor(DocumentDescriptor desc,
HandleImplementation handleBase) {
if (handleBase == null)
return;
handleBase.setFormat(desc.getFormat());
handleBase.setMimetype(desc.getMimetype());
handleBase.setByteLength(desc.getByteLength());
}
private void updateFormat(ContentDescriptor descriptor,
MultivaluedMap headers) {
updateFormat(descriptor, getHeaderFormat(headers));
}
private void updateFormat(ContentDescriptor descriptor, Format format) {
if (format != null) {
descriptor.setFormat(format);
}
}
private Format getHeaderFormat(MultivaluedMap headers) {
if (headers.containsKey("vnd.marklogic.document-format")) {
List values = headers.get("vnd.marklogic.document-format");
if (values != null) {
return Format.valueOf(values.get(0).toUpperCase());
}
}
return null;
}
private Format getHeaderFormat(BodyPart part) {
ContentDisposition contentDisposition = part.getContentDisposition();
if (part.getHeaders().containsKey("vnd.marklogic.document-format")) {
String value = part.getHeaders().getFirst("vnd.marklogic.document-format");
if (value != null) {
return Format.valueOf(value.toUpperCase());
}
} else if ( contentDisposition != null ) {
Map parameters = contentDisposition.getParameters();
if ( parameters != null && parameters.get("format") != null ) {
return Format.valueOf(parameters.get("format").toUpperCase());
}
} else if ( part.getHeaders().containsKey("Content-Type") ) {
String value = part.getHeaders().getFirst("Content-Type");
if (value != null) {
return Format.getFromMimetype(value);
}
}
return null;
}
private void updateMimetype(ContentDescriptor descriptor,
MultivaluedMap headers) {
updateMimetype(descriptor, getHeaderMimetype(headers));
}
private void updateMimetype(ContentDescriptor descriptor, String mimetype) {
if (mimetype != null) {
descriptor.setMimetype(mimetype);
}
}
private String getHeaderMimetype(Map> headers) {
if (headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
List values = headers.get(HttpHeaders.CONTENT_TYPE);
if (values != null) {
String contentType = values.get(0);
String mimetype = contentType.contains(";") ? contentType
.substring(0, contentType.indexOf(";")) : contentType;
// TODO: if "; charset=foo" set character set
if (mimetype != null && mimetype.length() > 0) {
return mimetype;
}
}
}
return null;
}
private void updateLength(ContentDescriptor descriptor,
MultivaluedMap headers) {
updateLength(descriptor, getHeaderLength(headers));
}
private void updateLength(ContentDescriptor descriptor, long length) {
descriptor.setByteLength(length);
}
private long getHeaderLength(MultivaluedMap headers) {
if (headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
List values = headers.get(HttpHeaders.CONTENT_LENGTH);
if (values != null) {
return Long.valueOf(values.get(0));
}
}
return ContentDescriptor.UNKNOWN_LENGTH;
}
private String getHeaderUri(ContentDisposition contentDisposition) {
if ( contentDisposition != null ) {
return contentDisposition.getFileName();
}
// if it's not found, just return null
return null;
}
private void updateVersion(DocumentDescriptor descriptor,
MultivaluedMap headers) {
long version = DocumentDescriptor.UNKNOWN_VERSION;
if (headers.containsKey("ETag")) {
List values = headers.get("ETag");
if (values != null) {
// trim the double quotes
String value = values.get(0);
version = Long.valueOf(value.substring(1, value.length() - 1));
}
}
descriptor.setVersion(version);
}
private WebResource.Builder addVersionHeader(DocumentDescriptor desc,
WebResource.Builder builder, String name) {
if (desc != null && desc instanceof DocumentDescriptorImpl
&& !((DocumentDescriptorImpl) desc).isInternal()) {
long version = desc.getVersion();
if (version != DocumentDescriptor.UNKNOWN_VERSION) {
return builder.header(name, "\"" + String.valueOf(version)
+ "\"");
}
}
return builder;
}
@Override
public T search(RequestLogger reqlog, Class as, QueryDefinition queryDef, String mimetype,
long start, long len, QueryView view, Transaction transaction
) throws ForbiddenUserException, FailedRequestException {
MultivaluedMap params = new MultivaluedMapImpl();
if (start > 1) {
params.add("start", Long.toString(start));
}
if (len > 0) {
params.add("pageLength", Long.toString(len));
}
if (view != null && view != QueryView.DEFAULT) {
if (view == QueryView.ALL) {
params.add("view", "all");
} else if (view == QueryView.RESULTS) {
params.add("view", "results");
} else if (view == QueryView.FACETS) {
params.add("view", "facets");
} else if (view == QueryView.METADATA) {
params.add("view", "metadata");
}
}
T entity = search(reqlog, as, queryDef, mimetype, transaction, params);
logRequest(
reqlog,
"searched starting at %s with length %s in %s transaction with %s mime type",
start, len, getTransactionId(transaction), mimetype);
return entity;
}
@Override
public T search(
RequestLogger reqlog, Class as, QueryDefinition queryDef, String mimetype, String view
) throws ForbiddenUserException, FailedRequestException {
MultivaluedMap params = new MultivaluedMapImpl();
if (view != null) {
params.add("view", view);
}
return search(reqlog, as, queryDef, mimetype, null, params);
}
private T search(RequestLogger reqlog, Class as, QueryDefinition queryDef, String mimetype,
Transaction transaction, MultivaluedMap params
) throws ForbiddenUserException, FailedRequestException {
JerseySearchRequest request = generateSearchRequest(reqlog, queryDef, mimetype, transaction, params);
ClientResponse response = request.getResponse();
if ( response == null ) return null;
T entity = response.hasEntity() ? response.getEntity(as) : null;
if (entity == null || (as != InputStream.class && as != Reader.class))
response.close();
return entity;
}
private JerseySearchRequest generateSearchRequest(RequestLogger reqlog, QueryDefinition queryDef,
String mimetype, Transaction transaction, MultivaluedMap params) {
if ( database != null ) {
if ( params == null ) params = new MultivaluedMapImpl();
addEncodedParam(params, "database", database);
}
return new JerseySearchRequest(reqlog, queryDef, mimetype, transaction, params);
}
private class JerseySearchRequest {
RequestLogger reqlog;
QueryDefinition queryDef;
String mimetype;
MultivaluedMap params;
Transaction transaction;
WebResource webResource = null;
WebResource.Builder builder = null;
String structure = null;
HandleImplementation baseHandle = null;
JerseySearchRequest(RequestLogger reqlog, QueryDefinition queryDef, String mimetype,
Transaction transaction, MultivaluedMap params) {
this.reqlog = reqlog;
this.queryDef = queryDef;
this.mimetype = mimetype;
this.transaction = transaction;
this.params = params != null ? params : new MultivaluedMapImpl();
addParams();
init();
}
void addParams() {
String directory = queryDef.getDirectory();
if (directory != null) {
addEncodedParam(params, "directory", directory);
}
addEncodedParam(params, "collection", queryDef.getCollections());
String optionsName = queryDef.getOptionsName();
if (optionsName != null && optionsName.length() > 0) {
addEncodedParam(params, "options", optionsName);
}
ServerTransform transform = queryDef.getResponseTransform();
if (transform != null) {
transform.merge(params);
}
if (transaction != null) {
params.add("txid", transaction.getTransactionId());
}
}
void init() {
if (queryDef instanceof RawQueryDefinition) {
if (logger.isDebugEnabled())
logger.debug("Raw search");
StructureWriteHandle handle =
((RawQueryDefinition) queryDef).getHandle();
baseHandle = HandleAccessor.checkHandle(handle, "search");
Format payloadFormat = baseHandle.getFormat();
if (payloadFormat == Format.UNKNOWN)
payloadFormat = null;
else if (payloadFormat != Format.XML && payloadFormat != Format.JSON)
throw new IllegalArgumentException(
"Cannot perform raw search for "+payloadFormat.name());
String payloadMimetype = baseHandle.getMimetype();
if (payloadFormat != null) {
if (payloadMimetype == null)
payloadMimetype = payloadFormat.getDefaultMimetype();
} else if (payloadMimetype == null) {
payloadMimetype = "application/xml";
}
String path = (queryDef instanceof RawQueryByExampleDefinition) ?
"qbe" : "search";
webResource = getConnection().path(path).queryParams(params);
builder = (payloadMimetype != null) ?
webResource.type(payloadMimetype).accept(mimetype) :
webResource.accept(mimetype);
} else if (queryDef instanceof StringQueryDefinition) {
String text = ((StringQueryDefinition) queryDef).getCriteria();
if (logger.isDebugEnabled())
logger.debug("Searching for {}", text);
if (text != null) {
addEncodedParam(params, "q", text);
}
webResource = getConnection().path("search").queryParams(params);
builder = webResource.type("application/xml").accept(mimetype);
} else if (queryDef instanceof KeyValueQueryDefinition) {
if (logger.isDebugEnabled())
logger.debug("Searching for keys/values");
Map pairs = ((KeyValueQueryDefinition) queryDef);
for (Map.Entry entry: pairs.entrySet()) {
ValueLocator loc = entry.getKey();
if (loc instanceof KeyLocator) {
addEncodedParam(params, "key", ((KeyLocator) loc).getKey());
} else {
ElementLocator eloc = (ElementLocator) loc;
params.add("element", eloc.getElement().toString());
if (eloc.getAttribute() != null) {
params.add("attribute", eloc.getAttribute().toString());
}
}
addEncodedParam(params, "value", entry.getValue());
}
webResource = getConnection().path("keyvalue").queryParams(params);
builder = webResource.accept(mimetype);
} else if (queryDef instanceof StructuredQueryDefinition) {
structure = ((StructuredQueryDefinition) queryDef).serialize();
if (logger.isDebugEnabled())
logger.debug("Searching for structure {}", structure);
webResource = getConnection().path("search").queryParams(params);
builder = webResource.type("application/xml").accept(mimetype);
} else if (queryDef instanceof CombinedQueryDefinition) {
structure = ((CombinedQueryDefinition) queryDef).serialize();
if (logger.isDebugEnabled())
logger.debug("Searching for combined query {}", structure);
webResource = getConnection().path("search").queryParams(params);
builder = webResource.type("application/xml").accept(mimetype);
} else if (queryDef instanceof DeleteQueryDefinition) {
if (logger.isDebugEnabled())
logger.debug("Searching for deletes");
webResource = getConnection().path("search").queryParams(params);
builder = webResource.accept(mimetype);
} else {
throw new UnsupportedOperationException("Cannot search with "
+ queryDef.getClass().getName());
}
addTransactionScopedCookies(builder, webResource, transaction);
}
ClientResponse getResponse() {
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
if (queryDef instanceof StringQueryDefinition) {
response = doGet(builder);
} else if (queryDef instanceof KeyValueQueryDefinition) {
response = doGet(builder);
} else if (queryDef instanceof StructuredQueryDefinition) {
response = doPost(reqlog, builder, structure, true);
} else if (queryDef instanceof CombinedQueryDefinition) {
response = doPost(reqlog, builder, structure, true);
} else if (queryDef instanceof DeleteQueryDefinition) {
response = doGet(builder);
} else if (queryDef instanceof RawQueryDefinition) {
response = doPost(reqlog, builder, baseHandle.sendContent(), true);
} else {
throw new UnsupportedOperationException("Cannot search with "
+ queryDef.getClass().getName());
}
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.NOT_FOUND) {
response.close();
return null;
}
if (status == ClientResponse.Status.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to search",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("search failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
return response;
}
}
@Override
public void deleteSearch(RequestLogger reqlog, DeleteQueryDefinition queryDef,
Transaction transaction) throws ForbiddenUserException,
FailedRequestException {
MultivaluedMap params = new MultivaluedMapImpl();
if (queryDef.getDirectory() != null) {
addEncodedParam(params, "directory", queryDef.getDirectory());
}
addEncodedParam(params, "collection", queryDef.getCollections());
if (transaction != null) {
params.add("txid", transaction.getTransactionId());
}
if ( database != null ) {
addEncodedParam(params, "database", database);
}
WebResource webResource = getConnection().path("search").queryParams(params);
WebResource.Builder builder = webResource.getRequestBuilder();
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.delete(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to delete",
extractErrorFields(response));
}
if (status != ClientResponse.Status.NO_CONTENT) {
throw new FailedRequestException("delete failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
response.close();
logRequest(
reqlog,
"deleted search results in %s transaction",
getTransactionId(transaction));
}
@Override
public void delete(RequestLogger logger, Transaction transaction, String... uris)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addEncodedParam(((RequestParametersImplementation) params).getMapImpl(), "uri", uris);
deleteResource(logger, "documents", transaction, params, null);
}
@Override
public T values(Class as, ValuesDefinition valDef, String mimetype,
long start, long pageLength, Transaction transaction
) throws ForbiddenUserException, FailedRequestException {
MultivaluedMap docParams = new MultivaluedMapImpl();
String optionsName = valDef.getOptionsName();
if (optionsName != null && optionsName.length() > 0) {
addEncodedParam(docParams, "options", optionsName);
}
if (valDef.getAggregate() != null) {
addEncodedParam(docParams, "aggregate", valDef.getAggregate());
}
if (valDef.getAggregatePath() != null) {
addEncodedParam(docParams, "aggregatePath",
valDef.getAggregatePath());
}
if (valDef.getView() != null) {
docParams.add("view", valDef.getView());
}
if (valDef.getDirection() != null) {
if (valDef.getDirection() == ValuesDefinition.Direction.ASCENDING) {
docParams.add("direction", "ascending");
} else {
docParams.add("direction", "descending");
}
}
if (valDef.getFrequency() != null) {
if (valDef.getFrequency() == ValuesDefinition.Frequency.FRAGMENT) {
docParams.add("frequency", "fragment");
} else {
docParams.add("frequency", "item");
}
}
if (start > 0) {
docParams.add("start", Long.toString(start));
if (pageLength > 0) {
docParams.add("pageLength", Long.toString(pageLength));
}
}
HandleImplementation baseHandle = null;
if (valDef.getQueryDefinition() != null) {
ValueQueryDefinition queryDef = valDef.getQueryDefinition();
if (optionsName == null) {
optionsName = queryDef.getOptionsName();
if (optionsName != null) {
addEncodedParam(docParams, "options", optionsName);
}
} else if (queryDef.getOptionsName() != null) {
if (optionsName != queryDef.getOptionsName()
&& logger.isWarnEnabled())
logger.warn("values definition options take precedence over query definition options");
}
if (queryDef.getCollections() != null) {
if (logger.isWarnEnabled())
logger.warn("collections scope ignored for values query");
}
if (queryDef.getDirectory() != null) {
if (logger.isWarnEnabled())
logger.warn("directory scope ignored for values query");
}
if (queryDef instanceof StringQueryDefinition) {
String text = ((StringQueryDefinition) queryDef).getCriteria();
if (text != null) {
addEncodedParam(docParams, "q", text);
}
} else if (queryDef instanceof StructuredQueryDefinition) {
String structure = ((StructuredQueryDefinition) queryDef)
.serialize();
if (structure != null) {
addEncodedParam(docParams, "structuredQuery", structure);
}
} else if (queryDef instanceof RawQueryDefinition) {
StructureWriteHandle handle = ((RawQueryDefinition) queryDef).getHandle();
baseHandle = HandleAccessor.checkHandle(handle, "values");
} else {
if (logger.isWarnEnabled())
logger.warn("unsupported query definition: "
+ queryDef.getClass().getName());
}
ServerTransform transform = queryDef.getResponseTransform();
if (transform != null) {
transform.merge(docParams);
}
}
if (transaction != null) {
docParams.add("txid", transaction.getTransactionId());
}
String uri = "values";
if (valDef.getName() != null) {
uri += "/" + valDef.getName();
}
WebResource webResource = makeWebResource(uri, docParams);
WebResource.Builder builder = makeBuilder(webResource, null, mimetype);
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = baseHandle == null ?
doGet(builder) :
doPost(null, builder.type(baseHandle.getMimetype()), baseHandle.sendContent(), baseHandle.isResendable());
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to search",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("search failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
T entity = response.hasEntity() ? response.getEntity(as) : null;
if (entity == null || (as != InputStream.class && as != Reader.class))
response.close();
return entity;
}
@Override
public T valuesList(Class as, ValuesListDefinition valDef,
String mimetype, Transaction transaction)
throws ForbiddenUserException, FailedRequestException {
MultivaluedMap docParams = new MultivaluedMapImpl();
String optionsName = valDef.getOptionsName();
if (optionsName != null && optionsName.length() > 0) {
addEncodedParam(docParams, "options", optionsName);
}
if (transaction != null) {
docParams.add("txid", transaction.getTransactionId());
}
String uri = "values";
WebResource webResource = makeWebResource(uri, docParams);
WebResource.Builder builder = makeBuilder(webResource, null, mimetype);
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.get(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to search",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("search failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
T entity = response.hasEntity() ? response.getEntity(as) : null;
if (entity == null || (as != InputStream.class && as != Reader.class))
response.close();
return entity;
}
@Override
public T optionsList(Class as, String mimetype, Transaction transaction)
throws ForbiddenUserException, FailedRequestException {
MultivaluedMap docParams = new MultivaluedMapImpl();
if (transaction != null) {
docParams.add("txid", transaction.getTransactionId());
}
String uri = "config/query";
WebResource webResource = getConnection().path(uri)
.queryParams(docParams);
WebResource.Builder builder = webResource.accept(mimetype);
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.get(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to search",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("search failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
T entity = response.hasEntity() ? response.getEntity(as) : null;
if (entity == null || (as != InputStream.class && as != Reader.class))
response.close();
return entity;
}
// namespaces, search options etc.
@Override
public T getValue(RequestLogger reqlog, String type, String key,
boolean isNullable, String mimetype, Class as)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Getting {}/{}", type, key);
WebResource.Builder builder = makeBuilder(type + "/" + key, null, null, mimetype);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.get(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status != ClientResponse.Status.OK) {
if (status == ClientResponse.Status.NOT_FOUND) {
response.close();
if (!isNullable)
throw new ResourceNotFoundException("Could not get " + type
+ "/" + key);
return null;
} else if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException("User is not allowed to read "
+ type, extractErrorFields(response));
else
throw new FailedRequestException(type + " read failed: "
+ status.getReasonPhrase(),
extractErrorFields(response));
}
logRequest(reqlog, "read %s value with %s key and %s mime type", type,
key, (mimetype != null) ? mimetype : null);
T entity = response.hasEntity() ? response.getEntity(as) : null;
if (entity == null || (as != InputStream.class && as != Reader.class))
response.close();
return (reqlog != null) ? reqlog.copyContent(entity) : entity;
}
@Override
public T getValues(RequestLogger reqlog, String type, String mimetype, Class as)
throws ForbiddenUserException, FailedRequestException {
return getValues(reqlog, type, null, mimetype, as);
}
@Override
public T getValues(RequestLogger reqlog, String type, RequestParameters extraParams,
String mimetype, Class as)
throws ForbiddenUserException, FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Getting {}", type);
MultivaluedMap requestParams = convertParams(extraParams);
WebResource.Builder builder = (requestParams == null) ?
getConnection().path(type).accept(mimetype) :
getConnection().path(type).queryParams(requestParams).accept(mimetype);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.get(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to read "
+ type, extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException(type + " read failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
logRequest(reqlog, "read %s values with %s mime type", type,
(mimetype != null) ? mimetype : null);
T entity = response.hasEntity() ? response.getEntity(as) : null;
if (entity == null || (as != InputStream.class && as != Reader.class))
response.close();
return (reqlog != null) ? reqlog.copyContent(entity) : entity;
}
@Override
public void postValue(RequestLogger reqlog, String type, String key,
String mimetype, Object value)
throws ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Posting {}/{}", type, key);
putPostValueImpl(reqlog, "post", type, key, null, mimetype, value,
ClientResponse.Status.CREATED);
}
@Override
public void postValue(RequestLogger reqlog, String type, String key,
RequestParameters extraParams
) throws ResourceNotResendableException, ForbiddenUserException, FailedRequestException
{
if (logger.isDebugEnabled())
logger.debug("Posting {}/{}", type, key);
putPostValueImpl(reqlog, "post", type, key, extraParams, null, null,
ClientResponse.Status.NO_CONTENT);
}
@Override
public void putValue(RequestLogger reqlog, String type, String key,
String mimetype, Object value) throws ResourceNotFoundException,
ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Putting {}/{}", type, key);
putPostValueImpl(reqlog, "put", type, key, null, mimetype, value,
ClientResponse.Status.NO_CONTENT, ClientResponse.Status.CREATED);
}
@Override
public void putValue(RequestLogger reqlog, String type, String key,
RequestParameters extraParams, String mimetype, Object value)
throws ResourceNotFoundException, ResourceNotResendableException,
ForbiddenUserException, FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Putting {}/{}", type, key);
putPostValueImpl(reqlog, "put", type, key, extraParams, mimetype,
value, ClientResponse.Status.NO_CONTENT);
}
private void putPostValueImpl(RequestLogger reqlog, String method,
String type, String key, RequestParameters extraParams,
String mimetype, Object value,
ClientResponse.Status... expectedStatuses) {
if (key != null) {
logRequest(reqlog, "writing %s value with %s key and %s mime type",
type, key, (mimetype != null) ? mimetype : null);
} else {
logRequest(reqlog, "writing %s values with %s mime type", type,
(mimetype != null) ? mimetype : null);
}
HandleImplementation handle = (value instanceof HandleImplementation) ?
(HandleImplementation) value : null;
MultivaluedMap requestParams = convertParams(extraParams);
String connectPath = null;
WebResource.Builder builder = null;
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
Object nextValue = (handle != null) ? handle.sendContent() : value;
Object sentValue = null;
if (nextValue instanceof OutputStreamSender) {
sentValue = new StreamingOutputImpl(
(OutputStreamSender) nextValue, reqlog);
} else {
if (reqlog != null && retry == 0)
sentValue = reqlog.copyContent(nextValue);
else
sentValue = nextValue;
}
boolean isStreaming = (isFirstRequest() || handle == null) ? isStreaming(sentValue)
: false;
boolean isResendable = (handle == null) ? !isStreaming :
handle.isResendable();
if (isFirstRequest() && !isResendable && isStreaming) {
nextDelay = makeFirstRequest(retry);
if (nextDelay != 0)
continue;
}
if ("put".equals(method)) {
if (builder == null) {
connectPath = (key != null) ? type + "/" + key : type;
WebResource resource = (requestParams == null) ?
getConnection().path(connectPath) :
getConnection().path(connectPath).queryParams(requestParams);
builder = (mimetype == null) ?
resource.getRequestBuilder() : resource.type(mimetype);
}
response = (sentValue == null) ?
builder.put(ClientResponse.class) :
builder.put(ClientResponse.class, sentValue);
} else if ("post".equals(method)) {
if (builder == null) {
connectPath = type;
WebResource resource = (requestParams == null) ?
getConnection().path(connectPath) :
getConnection().path(connectPath).queryParams(requestParams);
builder = (mimetype == null) ?
resource.getRequestBuilder() : resource.type(mimetype);
}
response = (sentValue == null) ?
builder.post(ClientResponse.class) :
builder.post(ClientResponse.class, sentValue);
} else {
throw new MarkLogicInternalException("unknown method type "
+ method);
}
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
response.close();
if (!isResendable) {
checkFirstRequest();
throw new ResourceNotResendableException(
"Cannot retry request for " + connectPath);
}
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException("User is not allowed to write "
+ type, extractErrorFields(response));
if (status == ClientResponse.Status.NOT_FOUND)
throw new ResourceNotFoundException(type + " not found for write",
extractErrorFields(response));
boolean statusOk = false;
for (ClientResponse.Status expectedStatus : expectedStatuses) {
statusOk = statusOk || (status == expectedStatus);
if (statusOk) {
break;
}
}
if (!statusOk) {
throw new FailedRequestException(type + " write failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
response.close();
}
@Override
public void deleteValue(RequestLogger reqlog, String type, String key)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Deleting {}/{}", type, key);
WebResource.Builder builder = getConnection().path(type + "/" + key)
.getRequestBuilder();
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.delete(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException("User is not allowed to delete "
+ type, extractErrorFields(response));
if (status == ClientResponse.Status.NOT_FOUND)
throw new ResourceNotFoundException(type + " not found for delete",
extractErrorFields(response));
if (status != ClientResponse.Status.NO_CONTENT)
throw new FailedRequestException("delete failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
response.close();
logRequest(reqlog, "deleted %s value with %s key", type, key);
}
@Override
public void deleteValues(RequestLogger reqlog, String type)
throws ForbiddenUserException, FailedRequestException {
if (logger.isDebugEnabled())
logger.debug("Deleting {}", type);
WebResource builder = getConnection().path(type);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = builder.delete(ClientResponse.class);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
if (status == ClientResponse.Status.FORBIDDEN)
throw new ForbiddenUserException("User is not allowed to delete "
+ type, extractErrorFields(response));
if (status != ClientResponse.Status.NO_CONTENT)
throw new FailedRequestException("delete failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
response.close();
logRequest(reqlog, "deleted %s values", type);
}
@Override
public R getResource(RequestLogger reqlog,
String path, Transaction transaction, RequestParameters params, R output)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
if ( params == null ) params = new RequestParameters();
if ( transaction != null ) params.add("txid", transaction.getTransactionId());
HandleImplementation outputBase = HandleAccessor.checkHandle(output,
"read");
String mimetype = outputBase.getMimetype();
Class as = outputBase.receiveAs();
WebResource webResource = makeGetWebResource(path, params, mimetype);
WebResource.Builder builder = makeBuilder(webResource, null, mimetype);
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = doGet(builder);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
checkStatus(response, status, "read", "resource", path,
ResponseStatus.OK_OR_NO_CONTENT);
if (as != null) {
outputBase.receiveContent(makeResult(reqlog, "read", "resource",
response, as));
} else {
response.close();
}
return output;
}
@Override
public ServiceResultIterator getIteratedResource(RequestLogger reqlog,
String path, Transaction transaction, RequestParameters params, String... mimetypes)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
return getIteratedResourceImpl(JerseyServiceResultIterator.class, reqlog, path, transaction, params, mimetypes);
}
private U getIteratedResourceImpl(Class clazz, RequestLogger reqlog,
String path, Transaction transaction, RequestParameters params, String... mimetypes)
throws ResourceNotFoundException, ForbiddenUserException,
FailedRequestException {
if ( params == null ) params = new RequestParameters();
if (transaction != null) params.add("txid", transaction.getTransactionId());
WebResource webResource = makeGetWebResource(path, params, null);
WebResource.Builder builder = makeBuilder(webResource, null, null);
addTransactionScopedCookies(builder, webResource, transaction);
MediaType multipartType = Boundary.addBoundary(MultiPartMediaTypes.MULTIPART_MIXED_TYPE);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = doGet(builder.accept(multipartType));
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
response.close();
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
checkStatus(response, status, "read", "resource", path,
ResponseStatus.OK_OR_NO_CONTENT);
return makeResults(clazz, reqlog, "read", "resource", response);
}
@Override
public R putResource(RequestLogger reqlog,
String path, Transaction transaction, RequestParameters params, AbstractWriteHandle input,
R output) throws ResourceNotFoundException,
ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
if ( params == null ) params = new RequestParameters();
if ( transaction != null ) params.add("txid", transaction.getTransactionId());
HandleImplementation inputBase = HandleAccessor.checkHandle(input,
"write");
HandleImplementation outputBase = HandleAccessor.checkHandle(output,
"read");
String inputMimetype = inputBase.getMimetype();
boolean isResendable = inputBase.isResendable();
String outputMimeType = null;
Class as = null;
if (outputBase != null) {
outputMimeType = outputBase.getMimetype();
as = outputBase.receiveAs();
}
WebResource webResource = makePutWebResource(path, params);
WebResource.Builder builder = makeBuilder(webResource, inputMimetype, outputMimeType);
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = doPut(reqlog, builder, inputBase.sendContent(),
!isResendable);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
response.close();
if (!isResendable) {
checkFirstRequest();
throw new ResourceNotResendableException(
"Cannot retry request for " + path);
}
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
checkStatus(response, status, "write", "resource", path,
ResponseStatus.OK_OR_CREATED_OR_NO_CONTENT);
if (as != null) {
outputBase.receiveContent(makeResult(reqlog, "write", "resource",
response, as));
} else {
response.close();
}
return output;
}
@Override
public R putResource(
RequestLogger reqlog, String path, Transaction transaction, RequestParameters params,
W[] input, R output) throws ResourceNotFoundException,
ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
if (input == null || input.length == 0)
throw new IllegalArgumentException(
"input not specified for multipart");
if ( params == null ) params = new RequestParameters();
if ( transaction != null ) params.add("txid", transaction.getTransactionId());
HandleImplementation outputBase = HandleAccessor.checkHandle(output,
"read");
String outputMimetype = outputBase.getMimetype();
Class as = outputBase.receiveAs();
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
MultiPart multiPart = new MultiPart();
boolean hasStreamingPart = addParts(multiPart, reqlog, input);
WebResource webResource = makePutWebResource(path, params);
WebResource.Builder builder = makeBuilder(webResource, multiPart, outputMimetype);
addTransactionScopedCookies(builder, webResource, transaction);
response = doPut(builder, multiPart, hasStreamingPart);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
response.close();
if (hasStreamingPart) {
throw new ResourceNotResendableException(
"Cannot retry request for " + path);
}
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
checkStatus(response, status, "write", "resource", path,
ResponseStatus.OK_OR_CREATED_OR_NO_CONTENT);
if (as != null) {
outputBase.receiveContent(makeResult(reqlog, "write", "resource",
response, as));
} else {
response.close();
}
return output;
}
@Override
public R postResource(RequestLogger reqlog,
String path, Transaction transaction, RequestParameters params,
AbstractWriteHandle input, R output) throws ResourceNotFoundException,
ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
if ( params == null ) params = new RequestParameters();
if ( transaction != null ) params.add("txid", transaction.getTransactionId());
HandleImplementation inputBase = HandleAccessor.checkHandle(input,
"write");
HandleImplementation outputBase = HandleAccessor.checkHandle(output,
"read");
String inputMimetype = inputBase.getMimetype();
String outputMimetype = outputBase == null ? null : outputBase.getMimetype();
boolean isResendable = inputBase.isResendable();
Class as = outputBase == null ? null : outputBase.receiveAs();
WebResource webResource = makePostWebResource(path, params);
WebResource.Builder builder = makeBuilder(webResource, inputMimetype, outputMimetype);
addTransactionScopedCookies(builder, webResource, transaction);
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
response = doPost(reqlog, builder, inputBase.sendContent(),
!isResendable);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
response.close();
if (!isResendable) {
checkFirstRequest();
throw new ResourceNotResendableException(
"Cannot retry request for " + path);
}
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
checkStatus(response, status, "apply", "resource", path,
ResponseStatus.OK_OR_CREATED_OR_NO_CONTENT);
if (as != null) {
outputBase.receiveContent(makeResult(reqlog, "apply", "resource",
response, as));
} else {
response.close();
}
return output;
}
@Override
public R postResource(
RequestLogger reqlog, String path, Transaction transaction, RequestParameters params,
W[] input, R output) throws ResourceNotFoundException,
ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
return postResource(reqlog, path, transaction, params, input, null, output);
}
@Override
public R postResource(
RequestLogger reqlog, String path, Transaction transaction, RequestParameters params,
W[] input, Map>[] headers, R output) throws ResourceNotFoundException,
ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
if ( params == null ) params = new RequestParameters();
if ( transaction != null ) params.add("txid", transaction.getTransactionId());
HandleImplementation outputBase = HandleAccessor.checkHandle(output, "read");
String outputMimetype = outputBase != null ? outputBase.getMimetype() : null;
Class as = outputBase != null ? outputBase.receiveAs() : null;
ClientResponse response = null;
ClientResponse.Status status = null;
long startTime = System.currentTimeMillis();
int nextDelay = 0;
int retry = 0;
for (; retry < minRetry || (System.currentTimeMillis() - startTime) < maxDelay; retry++) {
if (nextDelay > 0) {
try {
Thread.sleep(nextDelay);
} catch (InterruptedException e) {
}
}
MultiPart multiPart = new MultiPart();
boolean hasStreamingPart = addParts(multiPart, reqlog, null, input, headers);
WebResource webResource = makePostWebResource(path, params);
WebResource.Builder builder = makeBuilder(webResource, multiPart, outputMimetype);
addTransactionScopedCookies(builder, webResource, transaction);
response = doPost(builder, multiPart, hasStreamingPart);
status = response.getClientResponseStatus();
if (status != ClientResponse.Status.SERVICE_UNAVAILABLE) {
if (isFirstRequest())
setFirstRequest(false);
break;
}
MultivaluedMap responseHeaders = response.getHeaders();
String retryAfterRaw = responseHeaders.getFirst("Retry-After");
response.close();
if (hasStreamingPart) {
throw new ResourceNotResendableException(
"Cannot retry request for " + path);
}
int retryAfter = (retryAfterRaw != null) ? Integer.valueOf(retryAfterRaw) : -1;
nextDelay = Math.max(retryAfter, calculateDelay(randRetry, retry));
}
if (status == ClientResponse.Status.SERVICE_UNAVAILABLE) {
checkFirstRequest();
throw new FailedRequestException(
"Service unavailable and maximum retry period elapsed: "+
Math.round((System.currentTimeMillis() - startTime) / 1000)+
" seconds after "+retry+" retries");
}
checkStatus(response, status, "apply", "resource", path,
ResponseStatus.OK_OR_CREATED_OR_NO_CONTENT);
if (as != null) {
outputBase.receiveContent(makeResult(reqlog, "apply", "resource",
response, as));
} else {
response.close();
}
return output;
}
@Override
public void postBulkDocuments(
RequestLogger reqlog, DocumentWriteSet writeSet,
ServerTransform transform, Transaction transaction, Format defaultFormat)
throws ForbiddenUserException, FailedRequestException
{
postBulkDocuments(reqlog, writeSet, transform, transaction, defaultFormat, null, null);
}
@Override
public R postBulkDocuments(
RequestLogger reqlog, DocumentWriteSet writeSet,
ServerTransform transform, Transaction transaction, Format defaultFormat, R output,
String temporalCollection)
throws ForbiddenUserException, FailedRequestException
{
ArrayList writeHandles = new ArrayList();
ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy