Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.marklogic.client.impl.JerseyServices Maven / Gradle / Ivy
/*
* 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.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
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 ThreadSafeClientConnManager connMgr;
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;
if ( response.getStatus() == 401 ) {
FailedRequest failure = new FailedRequest();
failure.setMessageString("Unauthorized");
failure.setStatusString("Failed Auth");
return failure;
}
String responseBody = response.getEntity(String.class);
try {
InputStream is = new ByteArrayInputStream(responseBody.getBytes("UTF-8"));
FailedRequest handler = FailedRequest.getFailedRequest(
response.getStatus(), response.getType(), is);
if ( handler.getMessage() == null ) {
handler.setMessageString(responseBody);
}
return handler;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 is unsupported", 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 (connMgr != null) {
connMgr.shutdown();
connMgr = 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
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;
connMgr.shutdown();
connMgr = 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>> headerList = new ArrayList>>();
for ( DocumentWriteOperation write: writeSet ) {
HandleImplementation metadata =
HandleAccessor.checkHandle(write.getMetadata(), "write");
HandleImplementation content =
HandleAccessor.checkHandle(write.getContent(), "write");
if ( write.getOperationType() ==
DocumentWriteOperation.OperationType.DISABLE_METADATA_DEFAULT )
{
MultivaluedMap headers = new MultivaluedMapImpl();
headers.add(HttpHeaders.CONTENT_TYPE, metadata.getMimetype());
headers.add("Content-Disposition", "inline; category=metadata");
headerList.add(headers);
writeHandles.add(write.getMetadata());
} else if ( metadata != null ) {
MultivaluedMap headers = new MultivaluedMapImpl();
headers.add(HttpHeaders.CONTENT_TYPE, metadata.getMimetype());
if ( write.getOperationType() == DocumentWriteOperation.OperationType.METADATA_DEFAULT ) {
headers.add("Content-Disposition", "inline; category=metadata");
} else {
headers.add("Content-Disposition",
ContentDisposition
.type("attachment")
.fileName(write.getUri())
.build().toString() +
"; category=metadata"
);
}
headerList.add(headers);
writeHandles.add(write.getMetadata());
}
if ( content != null ) {
MultivaluedMap headers = new MultivaluedMapImpl();
String mimeType = content.getMimetype();
if ( mimeType == null && defaultFormat != null ) {
mimeType = defaultFormat.getDefaultMimetype();
}
headers.add(HttpHeaders.CONTENT_TYPE, mimeType);
headers.add("Content-Disposition",
ContentDisposition
.type("attachment")
.fileName(write.getUri())
.build().toString()
);
headerList.add(headers);
writeHandles.add(write.getContent());
}
}
RequestParameters params = new RequestParameters();
if (transform != null) {
transform.merge(params);
}
if (temporalCollection != null) params.add("temporal-collection", temporalCollection);
return postResource(
reqlog,
"documents",
transaction,
params,
(AbstractWriteHandle[]) writeHandles.toArray(new AbstractWriteHandle[0]),
(Map>[]) headerList.toArray(new HashMap[0]),
output);
}
public class JerseyEvalResultIterator implements EvalResultIterator {
private JerseyResultIterator iterator;
JerseyEvalResultIterator(JerseyResultIterator iterator) {
this.iterator = iterator;
}
@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 EvalResult next() {
if ( iterator == null ) throw new NoSuchElementException("No results available");
JerseyResult jerseyResult = iterator.next();
EvalResult result = new JerseyEvalResult(jerseyResult);
return result;
}
public void close() {
if ( iterator != null ) iterator.close();
}
}
public class JerseyEvalResult implements EvalResult {
private JerseyResult content;
public JerseyEvalResult(JerseyResult content) {
this.content = content;
}
@Override
public Format getFormat() {
return content.getFormat();
}
@Override
public EvalResult.Type getType() {
String contentType = content.getHeader("Content-Type");
String xPrimitive = content.getHeader("X-Primitive");
if ( contentType != null ) {
if ( "application/json".equals(contentType) ) {
if ( "null-node()".equals(xPrimitive) ) {
return EvalResult.Type.NULL;
} else {
return EvalResult.Type.JSON;
}
} else if ( "text/json".equals(contentType) ) {
return EvalResult.Type.JSON;
} else if ( "application/xml".equals(contentType) ) {
return EvalResult.Type.XML;
} else if ( "text/xml".equals(contentType) ) {
return EvalResult.Type.XML;
} else if ( "application/x-unknown-content-type".equals(contentType) &&
"binary()".equals(xPrimitive) )
{
return EvalResult.Type.BINARY;
} else if ( "application/octet-stream".equals(contentType) &&
"node()".equals(xPrimitive) )
{
return EvalResult.Type.BINARY;
}
}
if ( xPrimitive == null ) {
return EvalResult.Type.OTHER;
} else if ( "string".equals(xPrimitive) || "untypedAtomic".equals(xPrimitive) ) {
return EvalResult.Type.STRING;
} else if ( "boolean".equals(xPrimitive) ) {
return EvalResult.Type.BOOLEAN;
} else if ( "attribute()".equals(xPrimitive) ) {
return EvalResult.Type.ATTRIBUTE;
} else if ( "comment()".equals(xPrimitive) ) {
return EvalResult.Type.COMMENT;
} else if ( "processing-instruction()".equals(xPrimitive) ) {
return EvalResult.Type.PROCESSINGINSTRUCTION;
} else if ( "text()".equals(xPrimitive) ) {
return EvalResult.Type.TEXTNODE;
} else if ( "binary()".equals(xPrimitive) ) {
return EvalResult.Type.BINARY;
} else if ( "duration".equals(xPrimitive) ) {
return EvalResult.Type.DURATION;
} else if ( "date".equals(xPrimitive) ) {
return EvalResult.Type.DATE;
} else if ( "anyURI".equals(xPrimitive) ) {
return EvalResult.Type.ANYURI;
} else if ( "hexBinary".equals(xPrimitive) ) {
return EvalResult.Type.HEXBINARY;
} else if ( "base64Binary".equals(xPrimitive) ) {
return EvalResult.Type.BASE64BINARY;
} else if ( "dateTime".equals(xPrimitive) ) {
return EvalResult.Type.DATETIME;
} else if ( "decimal".equals(xPrimitive) ) {
return EvalResult.Type.DECIMAL;
} else if ( "double".equals(xPrimitive) ) {
return EvalResult.Type.DOUBLE;
} else if ( "float".equals(xPrimitive) ) {
return EvalResult.Type.FLOAT;
} else if ( "gDay".equals(xPrimitive) ) {
return EvalResult.Type.GDAY;
} else if ( "gMonth".equals(xPrimitive) ) {
return EvalResult.Type.GMONTH;
} else if ( "gMonthDay".equals(xPrimitive) ) {
return EvalResult.Type.GMONTHDAY;
} else if ( "gYear".equals(xPrimitive) ) {
return EvalResult.Type.GYEAR;
} else if ( "gYearMonth".equals(xPrimitive) ) {
return EvalResult.Type.GYEARMONTH;
} else if ( "integer".equals(xPrimitive) ) {
return EvalResult.Type.INTEGER;
} else if ( "QName".equals(xPrimitive) ) {
return EvalResult.Type.QNAME;
} else if ( "time".equals(xPrimitive) ) {
return EvalResult.Type.TIME;
}
return EvalResult.Type.OTHER;
}
@Override
public H get(H handle) {
if ( getType() == EvalResult.Type.NULL && handle instanceof StringHandle ) {
return (H) ((StringHandle) handle).with(null);
} else if ( getType() == EvalResult.Type.NULL && handle instanceof BytesHandle ) {
return (H) ((BytesHandle) handle).with(null);
} else {
return content.getContent(handle);
}
}
@Override
public T getAs(Class clazz) {
if ( getType() == EvalResult.Type.NULL ) return null;
if (clazz == null) throw new IllegalArgumentException("clazz cannot be null");
ContentHandle readHandle = DatabaseClientFactory.getHandleRegistry().makeHandle(clazz);
if ( readHandle == null ) return null;
readHandle = get(readHandle);
if ( readHandle == null ) return null;
return readHandle.get();
}
@Override
public String getString() {
if ( getType() == EvalResult.Type.NULL ) {
return null;
} else {
return content.getEntityAs(String.class);
}
}
@Override
public Number getNumber() {
if ( getType() == EvalResult.Type.DECIMAL ) return new BigDecimal(getString());
else if ( getType() == EvalResult.Type.DOUBLE ) return new Double(getString());
else if ( getType() == EvalResult.Type.FLOAT ) return new Float(getString());
// MarkLogic integers can be much larger than Java integers, so we'll use Long instead
else if ( getType() == EvalResult.Type.INTEGER ) return new Long(getString());
else return new BigDecimal(getString());
}
@Override
public Boolean getBoolean() {
return new Boolean(getString());
}
}
@Override
public EvalResultIterator postEvalInvoke(
RequestLogger reqlog, String code, String modulePath,
ServerEvaluationCallImpl.Context context,
Map variables, EditableNamespaceContext namespaces,
Transaction transaction)
throws ResourceNotFoundException, ResourceNotResendableException,
ForbiddenUserException, FailedRequestException
{
String formUrlEncodedPayload;
String path;
RequestParameters params = new RequestParameters();
try {
StringBuffer sb = new StringBuffer();
if ( context == ServerEvaluationCallImpl.Context.ADHOC_XQUERY ) {
path = "eval";
sb.append("xquery=");
sb.append(URLEncoder.encode(code, "UTF-8"));
} else if ( context == ServerEvaluationCallImpl.Context.ADHOC_JAVASCRIPT ) {
path = "eval";
sb.append("javascript=");
sb.append(URLEncoder.encode(code, "UTF-8"));
} else if ( context == ServerEvaluationCallImpl.Context.INVOKE ) {
path = "invoke";
sb.append("module=");
sb.append(URLEncoder.encode(modulePath, "UTF-8"));
} else {
throw new IllegalStateException("Invalid eval context: " + context);
}
if ( variables != null && variables.size() > 0 ) {
int i=0;
for ( String name : variables.keySet() ) {
String namespace = "";
String localname = name;
if ( namespaces != null ) {
for ( String prefix : namespaces.keySet() ) {
if ( name != null && prefix != null &&
name.startsWith(prefix + ":") )
{
localname = name.substring(prefix.length() + 1);
namespace = namespaces.get(prefix);
}
}
}
// set the variable namespace
sb.append("&evn" + i + "=");
sb.append(URLEncoder.encode(namespace, "UTF-8"));
// set the variable localname
sb.append("&evl" + i + "=");
sb.append(URLEncoder.encode(localname, "UTF-8"));
String value;
String type = null;
Object valueObject = variables.get(name);
if ( valueObject == null ) {
value = "null";
type = "null-node()";
} else if ( valueObject instanceof JacksonHandle ||
valueObject instanceof JacksonParserHandle ) {
JsonNode jsonNode = null;
if ( valueObject instanceof JacksonHandle ) {
jsonNode = ((JacksonHandle) valueObject).get();
} else if ( valueObject instanceof JacksonParserHandle ) {
jsonNode = ((JacksonParserHandle) valueObject).get().readValueAs(JsonNode.class);
}
value = jsonNode.toString();
type = getJsonType(jsonNode);
} else if ( valueObject instanceof AbstractWriteHandle ) {
value = HandleAccessor.contentAsString((AbstractWriteHandle) valueObject);
HandleImplementation valueBase = HandleAccessor.as((AbstractWriteHandle) valueObject);
Format format = valueBase.getFormat();
//TODO: figure out what type should be
// I see element() and document-node() are two valid types
if ( format == Format.XML ) {
type = "document-node()";
} else if ( format == Format.JSON ) {
JsonNode jsonNode = new JacksonParserHandle().getMapper().readTree(value);
type = getJsonType(jsonNode);
} else if ( format == Format.TEXT ) {
/* Comment next line until 32608 is resolved
type = "text()";
// until then, use the following line */
type = "xs:untypedAtomic";
} else if ( format == Format.BINARY ) {
throw new UnsupportedOperationException("Binary format is not supported for variables");
} else {
throw new UnsupportedOperationException("Undefined format is not supported for variables. " +
"Please set the format on your handle for variable " + name + ".");
}
} else if ( valueObject instanceof String ||
valueObject instanceof Boolean ||
valueObject instanceof Number ) {
value = valueObject.toString();
// when we send type "xs:untypedAtomic" via XDBC, the server attempts to intelligently decide
// how to cast the type
type = "xs:untypedAtomic";
} else {
throw new IllegalArgumentException("Variable with name=" +
name + " is of unsupported type" +
valueObject.getClass() + ". Supported types are String, Boolean, Number, " +
"or AbstractWriteHandle");
}
// set the variable value
sb.append("&evv" + i + "=");
sb.append(URLEncoder.encode(value, "UTF-8"));
// set the variable type
sb.append("&evt" + i + "=" + type);
i++;
}
}
formUrlEncodedPayload = sb.toString();
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 is unsupported", e);
} catch (IOException e) {
throw new MarkLogicIOException(e);
}
StringHandle input = new StringHandle(formUrlEncodedPayload)
.withMimetype("application/x-www-form-urlencoded");
return new JerseyEvalResultIterator( postIteratedResourceImpl(DefaultJerseyResultIterator.class,
reqlog, path, transaction, params, input) );
}
private String getJsonType(JsonNode jsonNode) {
if ( jsonNode instanceof ArrayNode ) {
return "json:array";
} else if ( jsonNode instanceof ObjectNode ) {
return "json:object";
} else {
throw new IllegalArgumentException("When using JacksonHandle or " +
"JacksonParserHandle with ServerEvaluationCall the content must be " +
"a valid array or object");
}
}
@Override
public ServiceResultIterator postIteratedResource(RequestLogger reqlog,
String path, Transaction transaction, RequestParameters params, AbstractWriteHandle input,
String... outputMimetypes) throws ResourceNotFoundException,
ResourceNotResendableException, ForbiddenUserException,
FailedRequestException {
return postIteratedResourceImpl(JerseyServiceResultIterator.class,
reqlog, path, transaction, params, input, outputMimetypes);
}
private U postIteratedResourceImpl(
Class clazz, RequestLogger reqlog,
String path, Transaction transaction, RequestParameters params,
AbstractWriteHandle input, String... outputMimetypes) 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");
String inputMimetype = inputBase.getMimetype();
boolean isResendable = inputBase.isResendable();
WebResource webResource = makePostWebResource(path, params);
WebResource.Builder builder = makeBuilder(webResource, inputMimetype, 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) {
}
}
Object value = inputBase.sendContent();
response = doPost(reqlog, builder.accept(multipartType), value, !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);
return makeResults(clazz, reqlog, "apply", "resource", response);
}
@Override
public ServiceResultIterator postIteratedResource(
RequestLogger reqlog, String path, Transaction transaction, RequestParameters params,
W[] input, String... outputMimetypes)
throws ResourceNotFoundException, ResourceNotResendableException,
ForbiddenUserException, FailedRequestException {
return postIteratedResourceImpl(JerseyServiceResultIterator.class,
reqlog, path, transaction, params, input, outputMimetypes);
}
private U postIteratedResourceImpl(
Class clazz, RequestLogger reqlog, String path, Transaction transaction,
RequestParameters params, W[] input, String... outputMimetypes)
throws ResourceNotFoundException, ResourceNotResendableException,
ForbiddenUserException, FailedRequestException {
if ( params == null ) params = new RequestParameters();
if ( transaction != null ) params.add("txid", transaction.getTransactionId());
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 = makePostWebResource(path, params);
WebResource.Builder builder = makeBuilder(
webResource,
multiPart,
Boundary.addBoundary(MultiPartMediaTypes.MULTIPART_MIXED_TYPE));
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);
return makeResults(clazz, reqlog, "apply", "resource", response);
}
@Override
public R deleteResource(
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 outputMimeType = null;
Class as = null;
if (outputBase != null) {
outputMimeType = outputBase.getMimetype();
as = outputBase.receiveAs();
}
WebResource webResource = makeDeleteWebResource(path, params);
WebResource.Builder builder = makeBuilder(webResource, null, 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 = doDelete(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, "delete", "resource", path,
ResponseStatus.OK_OR_NO_CONTENT);
if (as != null) {
outputBase.receiveContent(makeResult(reqlog, "delete", "resource",
response, as));
} else {
response.close();
}
return output;
}
private WebResource makeGetWebResource(String path,
RequestParameters params, Object mimetype) {
if (path == null)
throw new IllegalArgumentException("Read with null path");
if (logger.isDebugEnabled())
logger.debug(String.format("Getting %s as %s", path, mimetype));
return makeWebResource(path, convertParams(params));
}
private ClientResponse doGet(WebResource.Builder builder) {
ClientResponse response = builder.get(ClientResponse.class);
if (isFirstRequest())
setFirstRequest(false);
return response;
}
private WebResource makePutWebResource(String path,
RequestParameters params) {
if (path == null)
throw new IllegalArgumentException("Write with null path");
if (logger.isDebugEnabled())
logger.debug("Putting {}", path);
return makeWebResource(path, convertParams(params));
}
private ClientResponse doPut(RequestLogger reqlog,
WebResource.Builder builder, Object value, boolean isStreaming) {
if (value == null)
throw new IllegalArgumentException("Resource write with null value");
if (isFirstRequest() && isStreaming(value))
makeFirstRequest(0);
ClientResponse response = null;
if (value instanceof OutputStreamSender) {
response = builder
.put(ClientResponse.class, new StreamingOutputImpl(
(OutputStreamSender) value, reqlog));
} else {
if (reqlog != null)
response = builder.put(ClientResponse.class,
reqlog.copyContent(value));
else
response = builder.put(ClientResponse.class, value);
}
if (isFirstRequest())
setFirstRequest(false);
return response;
}
private ClientResponse doPut(WebResource.Builder builder,
MultiPart multiPart, boolean hasStreamingPart) {
if (isFirstRequest() && hasStreamingPart)
makeFirstRequest(0);
ClientResponse response = builder.put(ClientResponse.class, multiPart);
if (isFirstRequest())
setFirstRequest(false);
return response;
}
private WebResource makePostWebResource(String path, RequestParameters params) {
if (path == null)
throw new IllegalArgumentException("Apply with null path");
if (logger.isDebugEnabled())
logger.debug("Posting {}", path);
return makeWebResource(path, convertParams(params));
}
private ClientResponse doPost(RequestLogger reqlog,
WebResource.Builder builder, Object value, boolean isStreaming) {
if (isFirstRequest() && isStreaming(value))
makeFirstRequest(0);
ClientResponse response = null;
if (value instanceof OutputStreamSender) {
response = builder
.post(ClientResponse.class, new StreamingOutputImpl(
(OutputStreamSender) value, reqlog));
} else {
if (reqlog != null)
response = builder.post(ClientResponse.class,
reqlog.copyContent(value));
else
response = builder.post(ClientResponse.class, value);
}
if (isFirstRequest())
setFirstRequest(false);
return response;
}
private ClientResponse doPost(WebResource.Builder builder,
MultiPart multiPart, boolean hasStreamingPart) {
if (isFirstRequest() && hasStreamingPart)
makeFirstRequest(0);
ClientResponse response = builder.post(ClientResponse.class, multiPart);
if (isFirstRequest())
setFirstRequest(false);
return response;
}
private WebResource makeDeleteWebResource(String path, RequestParameters params) {
if (path == null)
throw new IllegalArgumentException("Delete with null path");
if (logger.isDebugEnabled())
logger.debug("Deleting {}", path);
return makeWebResource(path, convertParams(params));
}
private ClientResponse doDelete(WebResource.Builder builder) {
ClientResponse response = builder.delete(ClientResponse.class);
if (isFirstRequest())
setFirstRequest(false);
return response;
}
private MultivaluedMap convertParams(
RequestParameters params) {
if (params == null || params.size() == 0)
return null;
MultivaluedMap requestParams = new MultivaluedMapImpl();
for (Map.Entry> entry : params.entrySet()) {
addEncodedParam(requestParams, entry.getKey(), entry.getValue());
}
return requestParams;
}
private void addEncodedParam(MultivaluedMap params,
String key, List values) {
List encodedParams = encodeParamValues(values);
if (encodedParams != null && encodedParams.size() > 0)
params.put(key, encodedParams);
}
private void addEncodedParam(MultivaluedMap params,
String key, String[] values) {
List encodedParams = encodeParamValues(values);
if (encodedParams != null && encodedParams.size() > 0)
params.put(key, encodedParams);
}
private void addEncodedParam(MultivaluedMap params,
String key, String value) {
value = encodeParamValue(value);
if (value == null)
return;
params.add(key, value);
}
private List encodeParamValues(List oldValues) {
if (oldValues == null)
return null;
int oldSize = oldValues.size();
if (oldSize == 0)
return null;
List newValues = new ArrayList(oldSize);
for (String value : oldValues) {
String newValue = encodeParamValue(value);
if (newValue == null)
continue;
newValues.add(newValue);
}
return newValues;
}
private List encodeParamValues(String[] oldValues) {
if (oldValues == null)
return null;
int oldSize = oldValues.length;
if (oldSize == 0)
return null;
List newValues = new ArrayList(oldSize);
for (String value : oldValues) {
String newValue = encodeParamValue(value);
if (newValue == null)
continue;
newValues.add(newValue);
}
return newValues;
}
private String encodeParamValue(String value) {
if (value == null)
return null;
return UriComponent.encode(value, UriComponent.Type.QUERY_PARAM)
.replace("+", "%20");
}
private void addTransactionScopedCookies(WebResource.Builder builder, WebResource webResource,
Transaction transaction)
{
if ( transaction != null && transaction.getCookies() != null ) {
if ( builder == null ) {
throw new MarkLogicInternalException("no builder available to set cookies");
}
if ( webResource == null ) {
throw new MarkLogicInternalException("no webResource available to get the URI");
}
URI uri = webResource.getURI();
for ( NewCookie cookie : transaction.getCookies() ) {
// don't forward the cookie if it requires https and we're not using https
if ( cookie.isSecure() && ! "https".equals(uri.getScheme()) ) {
continue;
}
// don't forward the cookie if it requires a path and we're using a different path
if ( cookie.getPath() != null ) {
if ( uri.getPath() == null || ! uri.getPath().startsWith(cookie.getPath()) ) {
continue;
}
}
// don't forward the cookie if it requires a domain and we're using a different domain
if ( cookie.getDomain() != null ) {
if ( uri.getHost() == null || ! uri.getHost().equals(cookie.getDomain()) ) {
continue;
}
}
// don't forward the cookie if it has 0 for max age
if ( cookie.getMaxAge() == 0 ) { continue; }
// don't forward the cookie if it has a max age and we're past the max age
if ( cookie.getMaxAge() > 0 ) {
Calendar expiration = (Calendar) ((TransactionImpl) transaction).getCreatedTimestamp().clone();
expiration.roll(Calendar.SECOND, cookie.getMaxAge());
if ( System.currentTimeMillis() > expiration.getTimeInMillis() ) {
continue;
}
}
builder.cookie(cookie);
}
}
}
private boolean addParts(
MultiPart multiPart, RequestLogger reqlog, W[] input) {
return addParts(multiPart, reqlog, null, input, null);
}
private boolean addParts(
MultiPart multiPart, RequestLogger reqlog, String[] mimetypes,
W[] input) {
return addParts(multiPart, reqlog, null, input, null);
}
private boolean addParts(
MultiPart multiPart, RequestLogger reqlog, String[] mimetypes,
W[] input, Map>[] headers) {
if (mimetypes != null && mimetypes.length != input.length)
throw new IllegalArgumentException(
"Mismatch between count of mimetypes and input");
if (headers != null && headers.length != input.length)
throw new IllegalArgumentException(
"Mismatch between count of headers and input");
multiPart.setMediaType(new MediaType("multipart", "mixed"));
boolean hasStreamingPart = false;
for (int i = 0; i < input.length; i++) {
AbstractWriteHandle handle = input[i];
HandleImplementation handleBase = HandleAccessor.checkHandle(
handle, "write");
if (!hasStreamingPart)
hasStreamingPart = !handleBase.isResendable();
Object value = handleBase.sendContent();
String inputMimetype = null;
if ( mimetypes != null ) inputMimetype = mimetypes[i];
if ( inputMimetype == null && headers != null ) {
inputMimetype = getHeaderMimetype(headers[i]);
}
if ( inputMimetype == null ) inputMimetype = handleBase.getMimetype();
String[] typeParts = (inputMimetype != null && inputMimetype
.contains("/")) ? inputMimetype.split("/", 2) : null;
MediaType typePart = (typeParts != null) ? new MediaType(
typeParts[0], typeParts[1]) : MediaType.WILDCARD_TYPE;
BodyPart bodyPart = null;
if (value instanceof OutputStreamSender) {
bodyPart = new BodyPart(new StreamingOutputImpl(
(OutputStreamSender) value, reqlog), typePart);
} else {
if (reqlog != null)
bodyPart = new BodyPart(reqlog.copyContent(value), typePart);
else
bodyPart = new BodyPart(value, typePart);
}
if ( headers != null ) {
MultivaluedMap mutableHeaders = bodyPart.getHeaders();
mutableHeaders.putAll(headers[i]);
}
multiPart = multiPart.bodyPart(bodyPart);
}
return hasStreamingPart;
}
private WebResource makeWebResource(String path,
MultivaluedMap params) {
if ( params == null ) params = new MultivaluedMapImpl();
if ( database != null ) {
addEncodedParam(params, "database", database);
}
return getConnection().path(path).queryParams(params);
}
private WebResource.Builder makeBuilder(WebResource webResource,
Object inputMimetype, Object outputMimetype) {
WebResource.Builder builder = webResource.getRequestBuilder();
if (inputMimetype == null) {
} else if (inputMimetype instanceof String) {
builder = builder.type((String) inputMimetype);
} else if (inputMimetype instanceof MediaType) {
builder = builder.type((MediaType) inputMimetype);
} else if (inputMimetype instanceof MultiPart) {
builder = builder.type(Boundary.addBoundary(MultiPartMediaTypes.MULTIPART_MIXED_TYPE));
if (logger.isDebugEnabled())
logger.debug("Sending multipart for {}", webResource.getURI().getPath());
} else {
throw new IllegalArgumentException(
"Unknown input mimetype specifier "
+ inputMimetype.getClass().getName());
}
if (outputMimetype == null) {
} else if (outputMimetype instanceof String) {
builder = builder.accept((String) outputMimetype);
} else if (outputMimetype instanceof MediaType) {
builder = builder.accept((MediaType) outputMimetype);
} else {
throw new IllegalArgumentException(
"Unknown output mimetype specifier "
+ outputMimetype.getClass().getName());
}
return builder;
}
private WebResource.Builder makeBuilder(String path,
MultivaluedMap params, Object inputMimetype,
Object outputMimetype) {
return makeBuilder(makeWebResource(path, params), inputMimetype, outputMimetype);
}
private void checkStatus(ClientResponse response,
ClientResponse.Status status, String operation, String entityType,
String path, ResponseStatus expected) {
if (!expected.isExpected(status)) {
FailedRequest failure = extractErrorFields(response);
if (status == ClientResponse.Status.NOT_FOUND) {
throw new ResourceNotFoundException("Could not " + operation
+ " " + entityType + " at " + path,
failure);
}
if (status == ClientResponse.Status.FORBIDDEN) {
if (failure.getMessageCode().equals("RESTAPI-CONTENTNOVERSION")) {
throw new FailedRequestException("Content version required to " +
operation + " " + entityType + " at " + path, failure);
}
throw new ForbiddenUserException("User is not allowed to "
+ operation + " " + entityType + " at " + path,
failure);
}
throw new FailedRequestException("failed to " + operation + " "
+ entityType + " at " + path + ": "
+ status.getReasonPhrase(), failure);
}
}
private T makeResult(RequestLogger reqlog, String operation,
String entityType, ClientResponse response, Class as) {
if (as == null) {
return null;
}
logRequest(reqlog, "%s for %s", operation, entityType);
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;
}
private U makeResults(
Class clazz, RequestLogger reqlog,
String operation, String entityType, ClientResponse response) {
if ( response == null ) return null;
MultiPart entity = response.hasEntity() ?
response.getEntity(MultiPart.class) : null;
List partList = (entity == null) ? null : entity.getBodyParts();
Closeable closeable = new MultipartCloseable(response, entity);
return makeResults(clazz, reqlog, operation, entityType, partList, response, closeable);
}
private U makeResults(
Class clazz, RequestLogger reqlog,
String operation, String entityType, List partList, ClientResponse response,
Closeable closeable) {
logRequest(reqlog, "%s for %s", operation, entityType);
if ( response == null ) return null;
try {
java.lang.reflect.Constructor constructor =
clazz.getConstructor(JerseyServices.class, RequestLogger.class, List.class, Closeable.class);
JerseyResultIterator result = constructor.newInstance(this, reqlog, partList, closeable);
MultivaluedMap headers = response.getHeaders();
if (headers.containsKey("vnd.marklogic.start")) {
result.setStart(Long.parseLong(headers.get("vnd.marklogic.start").get(0)));
}
if (headers.containsKey("vnd.marklogic.pageLength")) {
result.setPageSize(Long.parseLong(headers.get("vnd.marklogic.pageLength").get(0)));
}
if (headers.containsKey("vnd.marklogic.result-estimate")) {
result.setTotalSize(Long.parseLong(headers.get("vnd.marklogic.result-estimate").get(0)));
}
return (U) result;
} catch (Throwable t) {
throw new MarkLogicInternalException("Error instantiating " + clazz.getName(), t);
}
}
private boolean isStreaming(Object value) {
return !(value instanceof String || value instanceof byte[] || value instanceof File);
}
private void logRequest(RequestLogger reqlog, String message,
Object... params) {
if (reqlog == null)
return;
PrintStream out = reqlog.getPrintStream();
if (out == null)
return;
if (params == null || params.length == 0) {
out.println(message);
} else {
out.format(message, params);
out.println();
}
}
private String stringJoin(Collection collection, String separator,
String defaultValue) {
if (collection == null || collection.size() == 0)
return defaultValue;
StringBuilder builder = null;
for (Object value : collection) {
if (builder == null)
builder = new StringBuilder();
else
builder.append(separator);
builder.append(value);
}
return (builder != null) ? builder.toString() : null;
}
private int calculateDelay(Random rand, int i) {
int min =
(i > 6) ? DELAY_CEILING :
(i == 0) ? DELAY_FLOOR :
DELAY_FLOOR + (1 << i) * DELAY_MULTIPLIER;
int range =
(i > 6) ? DELAY_FLOOR :
(i == 0) ? 2 * DELAY_MULTIPLIER :
(i == 6) ? DELAY_CEILING - min :
(1 << i) * DELAY_MULTIPLIER;
return min + randRetry.nextInt(range);
}
public class MultipartCloseable implements Closeable {
private ClientResponse response;
private MultiPart multiPart;
public MultipartCloseable(ClientResponse response, MultiPart multiPart) {
this.response = response;
this.multiPart = multiPart;
}
public void close() throws IOException {
if ( multiPart != null ) multiPart.close();
if ( response != null ) response.close();
}
}
public class JerseyResult {
private RequestLogger reqlog;
private BodyPart part;
private boolean extractedHeaders = false;
private MultivaluedMap headers = null;
private String uri;
private Format format;
private String mimetype;
private long length;
public JerseyResult(RequestLogger reqlog, BodyPart part) {
this.reqlog = reqlog;
this.part = part;
}
public T getEntityAs(Class clazz) {
return part.getEntityAs(clazz);
}
public R getContent(R handle) {
if (part == null)
throw new IllegalStateException("Content already retrieved");
HandleImplementation handleBase = HandleAccessor.as(handle);
extractHeaders();
updateFormat(handleBase, format);
updateMimetype(handleBase, mimetype);
updateLength(handleBase, length);
Object contentEntity = part.getEntityAs(handleBase.receiveAs());
handleBase.receiveContent((reqlog != null) ? reqlog
.copyContent(contentEntity) : contentEntity);
part = null;
reqlog = null;
return handle;
}
public T getContentAs(Class clazz) {
ContentHandle readHandle = DatabaseClientFactory.getHandleRegistry().makeHandle(clazz);
readHandle = getContent(readHandle);
if ( readHandle == null ) return null;
return readHandle.get();
}
public String getUri() {
extractHeaders();
return uri;
}
public Format getFormat() {
extractHeaders();
return format;
}
public String getMimetype() {
extractHeaders();
return mimetype;
}
public long getLength() {
extractHeaders();
return length;
}
public String getHeader(String name) {
extractHeaders();
return headers.getFirst(name);
}
private void extractHeaders() {
if (part == null || extractedHeaders)
return;
headers = part.getHeaders();
format = getHeaderFormat(part);
mimetype = getHeaderMimetype(headers);
length = getHeaderLength(headers);
uri = getHeaderUri(part.getContentDisposition());
extractedHeaders = true;
}
}
public class JerseyServiceResult extends JerseyResult implements ServiceResult {
public JerseyServiceResult(RequestLogger reqlog, BodyPart part) {
super(reqlog, part);
}
}
public class JerseyResultIterator {
private RequestLogger reqlog;
private Iterator partQueue;
private Class clazz;
private long start = -1;
private long size = -1;
private long pageSize = -1;
private long totalSize = -1;
private Closeable closeable;
public JerseyResultIterator(RequestLogger reqlog,
List partList, Class clazz, Closeable closeable) {
this.clazz = clazz;
this.reqlog = reqlog;
if (partList != null && partList.size() > 0) {
this.size = partList.size();
this.partQueue = new ConcurrentLinkedQueue(
partList).iterator();
} else {
this.size = 0;
}
this.closeable = closeable;
}
public long getStart() {
return start;
}
public JerseyResultIterator setStart(long start) {
this.start = start;
return this;
}
public long getSize() {
return size;
}
public JerseyResultIterator setSize(long size) {
this.size = new Long(size);
return this;
}
public long getPageSize() {
return pageSize;
}
public JerseyResultIterator setPageSize(long pageSize) {
this.pageSize = pageSize;
return this;
}
public long getTotalSize() {
return totalSize;
}
public JerseyResultIterator setTotalSize(long totalSize) {
this.totalSize = totalSize;
return this;
}
public boolean hasNext() {
if (partQueue == null)
return false;
boolean hasNext = partQueue.hasNext();
return hasNext;
}
public T next() {
if (partQueue == null)
return null;
try {
java.lang.reflect.Constructor constructor =
clazz.getConstructor(JerseyServices.class, RequestLogger.class, BodyPart.class);
return constructor.newInstance(new JerseyServices(), reqlog, partQueue.next());
} catch (Throwable t) {
throw new IllegalStateException("Error instantiating " + clazz.getName(), t);
}
}
public void remove() {
if (partQueue == null)
return;
partQueue.remove();
if (!partQueue.hasNext()) close();
}
public void close() {
partQueue = null;
reqlog = null;
if ( closeable != null ) {
try { closeable.close(); } catch (IOException e) {}
}
}
protected void finalize() throws Throwable {
close();
super.finalize();
}
}
public class JerseyServiceResultIterator
extends JerseyResultIterator
implements ServiceResultIterator
{
public JerseyServiceResultIterator(RequestLogger reqlog,
List partList, Closeable closeable) {
super(reqlog, partList, JerseyServiceResult.class, closeable);
}
}
public class DefaultJerseyResultIterator
extends JerseyResultIterator
implements Iterator
{
public DefaultJerseyResultIterator(RequestLogger reqlog,
List partList, Closeable closeable) {
super(reqlog, partList, JerseyResult.class, closeable);
}
}
public class JerseyDocumentRecord implements DocumentRecord {
private JerseyResult content;
private JerseyResult metadata;
public JerseyDocumentRecord(JerseyResult content, JerseyResult metadata) {
this.content = content;
this.metadata = metadata;
}
public JerseyDocumentRecord(JerseyResult content) {
this.content = content;
}
public String getUri() {
if ( content == null && metadata != null ) {
return metadata.getUri();
} else if ( content != null ) {
return content.getUri();
} else {
throw new IllegalStateException("Missing both content and metadata!");
}
}
public Format getFormat() {
return content.getFormat();
}
public String getMimetype() {
return content.getMimetype();
}
public T getMetadata(T metadataHandle) {
if ( metadata == null ) throw new IllegalStateException(
"getMetadata called when no metadata is available");
return metadata.getContent(metadataHandle);
}
public T getMetadataAs(Class as) {
if ( as == null ) throw new IllegalStateException(
"getMetadataAs cannot accept null");
return metadata.getContentAs(as);
}
public T getContent(T contentHandle) {
if ( content == null ) throw new IllegalStateException(
"getContent called when no content is available");
return content.getContent(contentHandle);
}
public T getContentAs(Class as) {
if ( as == null ) throw new IllegalStateException(
"getContentAs cannot accept null");
return content.getContentAs(as);
}
}
@Override
public HttpClient getClientImplementation() {
if (client == null)
return null;
return client.getClientHandler().getHttpClient();
}
@Override
public T suggest(Class as, SuggestDefinition suggestionDef) {
MultivaluedMap params = new MultivaluedMapImpl();
String suggestCriteria = suggestionDef.getStringCriteria();
String[] queries = suggestionDef.getQueryStrings();
String optionsName = suggestionDef.getOptionsName();
Integer limit = suggestionDef.getLimit();
Integer cursorPosition = suggestionDef.getCursorPosition();
if (suggestCriteria != null) {
params.add("partial-q", suggestCriteria);
}
if (optionsName != null) {
params.add("options", optionsName);
}
if (limit != null) {
params.add("limit", Long.toString(limit));
}
if (cursorPosition != null) {
params.add("cursor-position", Long.toString(cursorPosition));
}
if (queries != null) {
for (String stringQuery : queries) {
params.add("q", stringQuery);
}
}
WebResource.Builder builder = null;
builder = makeBuilder("suggest", params, null, "application/xml");
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 get suggestions",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("Suggest call 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 InputStream match(StructureWriteHandle document,
String[] candidateRules, String mimeType, ServerTransform transform) {
MultivaluedMap params = new MultivaluedMapImpl();
HandleImplementation baseHandle = HandleAccessor.checkHandle(document, "match");
if (candidateRules != null) {
for (String candidateRule : candidateRules) {
params.add("rule", candidateRule);
}
}
if (transform != null) {
transform.merge(params);
}
WebResource.Builder builder = null;
builder = makeBuilder("alert/match", params, "application/xml", 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 = doPost(null, builder, baseHandle.sendContent(), false);
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 match",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("match failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
InputStream entity = response.hasEntity() ?
response.getEntity(InputStream.class) : null;
if (entity == null)
response.close();
return entity;
}
@Override
public InputStream match(QueryDefinition queryDef,
long start, long pageLength, String[] candidateRules, ServerTransform transform) {
if (queryDef == null) {
throw new IllegalArgumentException("Cannot match null query");
}
MultivaluedMap params = new MultivaluedMapImpl();
if (start > 1) {
params.add("start", Long.toString(start));
}
if (pageLength >= 0) {
params.add("pageLength", Long.toString(pageLength));
}
if (transform != null) {
transform.merge(params);
}
if (candidateRules.length > 0) {
for (String candidateRule : candidateRules) {
params.add("rule", candidateRule);
}
}
if (queryDef.getOptionsName() != null) {
params.add("options", queryDef.getOptionsName());
}
WebResource.Builder builder = null;
String structure = null;
HandleImplementation baseHandle = null;
if (queryDef instanceof RawQueryDefinition) {
StructureWriteHandle handle = ((RawQueryDefinition) queryDef).getHandle();
baseHandle = HandleAccessor.checkHandle(handle, "match");
if (logger.isDebugEnabled())
logger.debug("Searching for structure {}", structure);
builder = makeBuilder("alert/match", params, "application/xml", "application/xml");
} else if (queryDef instanceof StringQueryDefinition) {
String text = ((StringQueryDefinition) queryDef).getCriteria();
if (logger.isDebugEnabled())
logger.debug("Searching for {} in transaction {}", text);
if (text != null) {
addEncodedParam(params, "q", text);
}
builder = makeBuilder("alert/match", params, null, "application/xml");
} else if (queryDef instanceof StructuredQueryDefinition) {
structure = ((StructuredQueryDefinition) queryDef).serialize();
if (logger.isDebugEnabled())
logger.debug("Searching for structure {} in transaction {}",
structure);
builder = makeBuilder("alert/match", params, "application/xml", "application/xml");
} else {
throw new UnsupportedOperationException("Cannot match with "
+ queryDef.getClass().getName());
}
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 = builder.get(ClientResponse.class);
} else if (queryDef instanceof StructuredQueryDefinition) {
response = builder.post(ClientResponse.class, structure);
} else if (queryDef instanceof RawQueryDefinition) {
response = doPost(null, builder, baseHandle.sendContent(), false);
} else {
throw new UnsupportedOperationException("Cannot match 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.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to match",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("match failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
InputStream entity = response.hasEntity() ?
response.getEntity(InputStream.class) : null;
if (entity == null)
response.close();
return entity;
}
@Override
public InputStream match(String[] docIds, String[] candidateRules, ServerTransform transform) {
MultivaluedMap params = new MultivaluedMapImpl();
if (docIds.length > 0) {
for (String docId : docIds) {
params.add("uri", docId);
}
}
if (candidateRules.length > 0) {
for (String candidateRule : candidateRules) {
params.add("rule", candidateRule);
}
}
if (transform != null) {
transform.merge(params);
}
WebResource.Builder builder = makeBuilder("alert/match", params, "application/xml", "application/xml");
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");
}
if (status == ClientResponse.Status.FORBIDDEN) {
throw new ForbiddenUserException("User is not allowed to match",
extractErrorFields(response));
}
if (status != ClientResponse.Status.OK) {
throw new FailedRequestException("match failed: "
+ status.getReasonPhrase(), extractErrorFields(response));
}
InputStream entity = response.hasEntity() ?
response.getEntity(InputStream.class) : null;
if (entity == null)
response.close();
return entity;
}
private void addGraphUriParam(RequestParameters params, String uri) {
if ( uri == null || uri.equals(GraphManager.DEFAULT_GRAPH) ) {
params.add("default", "");
} else {
params.add("graph", uri);
}
}
private void addPermsParams(RequestParameters params, GraphPermissions permissions) {
if ( permissions != null ) {
for ( String role : permissions.keySet() ) {
if ( permissions.get(role) != null ) {
for ( Capability capability : permissions.get(role) ) {
params.add("perm:" + role, capability.toString().toLowerCase());
}
}
}
}
}
@Override
public R getGraphUris(RequestLogger reqlog, R output)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
return getResource(reqlog, "graphs", null, null, output);
}
@Override
public R readGraph(RequestLogger reqlog, String uri, R output,
Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
return getResource(reqlog, "graphs", transaction, params, output);
}
@Override
public void writeGraph(RequestLogger reqlog, String uri,
AbstractWriteHandle input, GraphPermissions permissions, Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
addPermsParams(params, permissions);
putResource(reqlog, "graphs", transaction, params, input, null);
}
@Override
public void writeGraphs(RequestLogger reqlog, AbstractWriteHandle input, Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
putResource(reqlog, "graphs", transaction, params, input, null);
}
@Override
public void mergeGraph(RequestLogger reqlog, String uri,
AbstractWriteHandle input, GraphPermissions permissions, Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
addPermsParams(params, permissions);
postResource(reqlog, "graphs", transaction, params, input, null);
}
@Override
public void mergeGraphs(RequestLogger reqlog, AbstractWriteHandle input, Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
postResource(reqlog, "graphs", transaction, params, input, null);
}
@Override
public R getPermissions(RequestLogger reqlog, String uri,
R output,Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
params.add("category", "permissions");
return getResource(reqlog, "graphs", transaction, params, output);
}
@Override
public void deletePermissions(RequestLogger reqlog, String uri, Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
params.add("category", "permissions");
deleteResource(reqlog, "graphs", transaction, params, null);
}
@Override
public void writePermissions(RequestLogger reqlog, String uri,
AbstractWriteHandle permissions, Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
params.add("category", "permissions");
putResource(reqlog, "graphs", transaction, params, permissions, null);
}
@Override
public void mergePermissions(RequestLogger reqlog, String uri,
AbstractWriteHandle permissions, Transaction transaction)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
params.add("category", "permissions");
postResource(reqlog, "graphs", transaction, params, permissions, null);
}
@Override
public Object deleteGraph(RequestLogger reqlog, String uri, Transaction transaction)
throws ForbiddenUserException, FailedRequestException
{
RequestParameters params = new RequestParameters();
addGraphUriParam(params, uri);
return deleteResource(reqlog, "graphs", transaction, params, null);
}
@Override
public void deleteGraphs(RequestLogger reqlog, Transaction transaction)
throws ForbiddenUserException, FailedRequestException
{
deleteResource(reqlog, "graphs", transaction, null, null);
}
@Override
public R getThings(RequestLogger reqlog, String[] iris, R output)
throws ResourceNotFoundException, ForbiddenUserException, FailedRequestException
{
if ( iris == null ) throw new IllegalArgumentException("iris cannot be null");
RequestParameters params = new RequestParameters();
for ( String iri : iris ) {
params.add("iri", iri);
}
return getResource(reqlog, "graphs/things", null, params, output);
}
@Override
public R executeSparql(RequestLogger reqlog,
SPARQLQueryDefinition qdef, R output, long start, long pageLength,
Transaction transaction, boolean isUpdate)
{
if ( qdef == null ) throw new IllegalArgumentException("qdef cannot be null");
if ( output == null ) throw new IllegalArgumentException("output cannot be null");
RequestParameters params = new RequestParameters();
if (start > 1) params.add("start", Long.toString(start));
if (pageLength >= 0) params.add("pageLength", Long.toString(pageLength));
if (qdef.getOptimizeLevel() >= 0) {
params.add("optimize", Integer.toString(qdef.getOptimizeLevel()));
}
if (qdef.getCollections() != null ) {
for ( String collection : qdef.getCollections() ) {
params.add("collection", collection);
}
}
addPermsParams(params, qdef.getUpdatePermissions());
String sparql = qdef.getSparql();
SPARQLBindings bindings = qdef.getBindings();
for ( String bindingName : bindings.keySet() ) {
String paramName = "bind:" + bindingName;
String typeOrLang = "";
for ( SPARQLBinding binding : bindings.get(bindingName) ) {
if ( binding.getDatatype() != null ) {
typeOrLang = ":" + binding.getDatatype();
} else if ( binding.getLanguageTag() != null ) {
typeOrLang = "@" + binding.getLanguageTag().toLanguageTag();
}
params.add(paramName + typeOrLang, binding.getValue());
}
}
QueryDefinition constrainingQuery = qdef.getConstrainingQueryDefinition();
StructureWriteHandle input;
if ( constrainingQuery != null ) {
if (qdef.getOptionsName()!= null && qdef.getOptionsName().length() > 0) {
params.add("options", qdef.getOptionsName());
}
if ( constrainingQuery instanceof RawCombinedQueryDefinition ) {
CombinedQueryDefinition combinedQdef = new CombinedQueryBuilderImpl().combine(
(RawCombinedQueryDefinition) constrainingQuery, null, null, sparql);
Format format = combinedQdef.getFormat();
input = new StringHandle(combinedQdef.serialize()).withFormat(format);
} else if ( constrainingQuery instanceof RawStructuredQueryDefinition ) {
CombinedQueryDefinition combinedQdef = new CombinedQueryBuilderImpl().combine(
(RawStructuredQueryDefinition) constrainingQuery, null, null, sparql);
Format format = combinedQdef.getFormat();
input = new StringHandle(combinedQdef.serialize()).withFormat(format);
} else if ( constrainingQuery instanceof StringQueryDefinition ||
constrainingQuery instanceof StructuredQueryDefinition ) {
String stringQuery = constrainingQuery instanceof StringQueryDefinition ?
((StringQueryDefinition) constrainingQuery).getCriteria() : null;
StructuredQueryDefinition structuredQuery =
constrainingQuery instanceof StructuredQueryDefinition ?
(StructuredQueryDefinition) constrainingQuery : null;
CombinedQueryDefinition combinedQdef = new CombinedQueryBuilderImpl().combine(
structuredQuery, null, stringQuery, sparql);
input = new StringHandle(combinedQdef.serialize()).withMimetype("application/xml");
} else {
throw new IllegalArgumentException(
"Constraining query must be of type SPARQLConstrainingQueryDefinition");
}
} else {
String mimetype = isUpdate ? "application/sparql-update" : "application/sparql-query";
input = new StringHandle(sparql).withMimetype(mimetype);
}
if (qdef.getBaseUri() != null) {
params.add("base", qdef.getBaseUri());
}
if (qdef.getDefaultGraphUris() != null) {
for (String defaultGraphUri : qdef.getDefaultGraphUris()) {
params.add("default-graph-uri", defaultGraphUri);
}
}
if (qdef.getNamedGraphUris() != null) {
for (String namedGraphUri : qdef.getNamedGraphUris()) {
params.add("named-graph-uri", namedGraphUri);
}
}
if (qdef.getUsingGraphUris() != null) {
for (String usingGraphUri : qdef.getUsingGraphUris()) {
params.add("using-graph-uri", usingGraphUri);
}
}
if (qdef.getUsingNamedGraphUris() != null) {
for (String usingNamedGraphUri : qdef.getUsingNamedGraphUris()) {
params.add("using-named-graph-uri", usingNamedGraphUri);
}
}
// rulesets
if (qdef.getRulesets() != null) {
for (SPARQLRuleset ruleset : qdef.getRulesets()) {
params.add("ruleset", ruleset.getName());
}
}
if (qdef.getIncludeDefaultRulesets() != null) {
params.add("default-rulesets", qdef.getIncludeDefaultRulesets() ? "include" : "exclude");
}
return postResource(reqlog, "/graphs/sparql", transaction, params, input, output);
}
private String getTransactionId(Transaction transaction) {
if ( transaction == null ) return null;
return transaction.getTransactionId();
}
}