Java tutorial
package net.ravendb.client.connection; import static net.ravendb.client.connection.RavenUrlExtensions.indexes; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import net.ravendb.abstractions.basic.CleanCloseable; import net.ravendb.abstractions.basic.EventHandler; import net.ravendb.abstractions.basic.Reference; import net.ravendb.abstractions.basic.SharpEnum; import net.ravendb.abstractions.closure.Action2; import net.ravendb.abstractions.closure.Action3; import net.ravendb.abstractions.closure.Function0; import net.ravendb.abstractions.closure.Function1; import net.ravendb.abstractions.closure.Function3; import net.ravendb.abstractions.commands.ICommandData; import net.ravendb.abstractions.commands.PatchCommandData; import net.ravendb.abstractions.commands.ScriptedPatchCommandData; import net.ravendb.abstractions.connection.ErrorResponseException; import net.ravendb.abstractions.connection.OperationCredentials; import net.ravendb.abstractions.data.Attachment; import net.ravendb.abstractions.data.AttachmentInformation; import net.ravendb.abstractions.data.BatchResult; import net.ravendb.abstractions.data.BuildNumber; import net.ravendb.abstractions.data.BulkInsertOptions; import net.ravendb.abstractions.data.BulkOperationOptions; import net.ravendb.abstractions.data.Constants; import net.ravendb.abstractions.data.DatabaseStatistics; import net.ravendb.abstractions.data.Etag; import net.ravendb.abstractions.data.Facet; import net.ravendb.abstractions.data.FacetQuery; import net.ravendb.abstractions.data.FacetResult; import net.ravendb.abstractions.data.FacetResults; import net.ravendb.abstractions.data.GetRequest; import net.ravendb.abstractions.data.GetResponse; import net.ravendb.abstractions.data.HttpMethods; import net.ravendb.abstractions.data.IndexQuery; import net.ravendb.abstractions.data.JsonDocument; import net.ravendb.abstractions.data.JsonDocumentMetadata; import net.ravendb.abstractions.data.MoreLikeThisQuery; import net.ravendb.abstractions.data.MultiLoadResult; import net.ravendb.abstractions.data.PatchRequest; import net.ravendb.abstractions.data.PatchResult; import net.ravendb.abstractions.data.PutResult; import net.ravendb.abstractions.data.QueryHeaderInformation; import net.ravendb.abstractions.data.QueryResult; import net.ravendb.abstractions.data.ScriptedPatchRequest; import net.ravendb.abstractions.data.SuggestionQuery; import net.ravendb.abstractions.data.SuggestionQueryResult; import net.ravendb.abstractions.exceptions.BadRequestException; import net.ravendb.abstractions.exceptions.ConcurrencyException; import net.ravendb.abstractions.exceptions.DocumentDoesNotExistsException; import net.ravendb.abstractions.exceptions.IndexCompilationException; import net.ravendb.abstractions.exceptions.JsonReaderException; import net.ravendb.abstractions.exceptions.TransformCompilationException; import net.ravendb.abstractions.extensions.ExceptionExtensions; import net.ravendb.abstractions.extensions.MetadataExtensions; import net.ravendb.abstractions.indexing.IndexDefinition; import net.ravendb.abstractions.indexing.IndexMergeResults; import net.ravendb.abstractions.indexing.NumberUtil; import net.ravendb.abstractions.indexing.TransformerDefinition; import net.ravendb.abstractions.json.linq.JTokenType; import net.ravendb.abstractions.json.linq.RavenJArray; import net.ravendb.abstractions.json.linq.RavenJObject; import net.ravendb.abstractions.json.linq.RavenJToken; import net.ravendb.abstractions.json.linq.RavenJValue; import net.ravendb.abstractions.replication.ReplicationDocument; import net.ravendb.abstractions.util.BomUtils; import net.ravendb.abstractions.util.NetDateFormat; import net.ravendb.client.RavenPagingInformation; import net.ravendb.client.changes.IDatabaseChanges; import net.ravendb.client.connection.ReplicationInformer.FailoverStatusChangedEventArgs; import net.ravendb.client.connection.implementation.HttpJsonRequest; import net.ravendb.client.connection.implementation.HttpJsonRequestFactory; import net.ravendb.client.connection.profiling.ProfilingInformation; import net.ravendb.client.document.DocumentConvention; import net.ravendb.client.document.ILowLevelBulkInsertOperation; import net.ravendb.client.document.JsonSerializer; import net.ravendb.client.document.RemoteBulkInsertOperation; import net.ravendb.client.exceptions.ConflictException; import net.ravendb.client.exceptions.ServerRequestError; import net.ravendb.client.extensions.HttpJsonRequestExtension; import net.ravendb.client.extensions.MultiDatabase; import net.ravendb.client.indexes.IndexDefinitionBuilder; import net.ravendb.client.listeners.IDocumentConflictListener; import net.ravendb.client.utils.UrlUtils; import net.ravendb.imports.json.JsonConvert; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.util.EntityUtils; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.codehaus.jackson.map.JsonDeserializer; import org.codehaus.jackson.map.ObjectMapper; public class ServerClient implements IDatabaseCommands { private final ProfilingInformation profilingInformation; private final IDocumentConflictListener[] conflictListeners; protected String url; private String rootUrl; private OperationCredentials credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication; final DocumentConvention convention; protected Map<String, String> operationsHeaders = new HashMap<>(); protected final HttpJsonRequestFactory jsonRequestFactory; private final UUID sessionId; private final Function1<String, IDocumentStoreReplicationInformer> replicationInformerGetter; private final String databaseName; private final IDocumentStoreReplicationInformer replicationInformer; protected int requestCount; protected int readStripingBase; private boolean resolvingConflict; private boolean resolvingConflictRetries; private boolean expect100Continue = false; /** * @return the url */ public String getUrl() { return url; } /** * @return the replicationInformer */ public IDocumentStoreReplicationInformer getReplicationInformer() { return replicationInformer; } @Override public OperationCredentials getPrimaryCredentials() { return credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication; } public ServerClient(String url, DocumentConvention convention, OperationCredentials credentials, HttpJsonRequestFactory httpJsonRequestFactory, UUID sessionId, Function1<String, IDocumentStoreReplicationInformer> replicationInformerGetter, String databaseName, IDocumentConflictListener[] conflictListeners, boolean incrementReadStripe) { this.profilingInformation = ProfilingInformation.createProfilingInformation(sessionId); this.url = url; if (this.url.endsWith("/")) { this.url = this.url.substring(0, this.url.length() - 1); } rootUrl = this.url; int databasesIndex = rootUrl.indexOf("/databases/"); if (databasesIndex > 0) { rootUrl = rootUrl.substring(0, databasesIndex); } this.jsonRequestFactory = httpJsonRequestFactory; this.sessionId = sessionId; this.convention = convention; this.credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication = credentials; this.databaseName = databaseName; this.conflictListeners = conflictListeners; this.replicationInformerGetter = replicationInformerGetter; this.replicationInformer = replicationInformerGetter.apply(databaseName); this.readStripingBase = replicationInformer.getReadStripingBase(incrementReadStripe); replicationInformer.updateReplicationInformationIfNeeded(this); } @Override public Collection<String> getIndexNames(final int start, final int pageSize) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, Collection<String>>() { @Override public Collection<String> apply(OperationMetadata operationMetadata) { return directGetIndexNames(start, pageSize, operationMetadata); } }); } protected Collection<String> directGetIndexNames(int start, int pageSize, OperationMetadata operationMetadata) { try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest(new CreateHttpJsonRequestParams( this, RavenUrlExtensions.indexNames(operationMetadata.getUrl(), start, pageSize), HttpMethods.GET, null, operationMetadata.getCredentials(), convention))) { RavenJArray json = (RavenJArray) request .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()) .readResponseJson(); return json.values(String.class); } } @Override public Collection<IndexDefinition> getIndexes(final int start, final int pageSize) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, Collection<IndexDefinition>>() { @Override public Collection<IndexDefinition> apply(OperationMetadata operationMetadata) { return directGetIndexes(start, pageSize, operationMetadata); } }); } protected Collection<IndexDefinition> directGetIndexes(int start, int pageSize, OperationMetadata operationMetadata) { String operationUrl = operationMetadata.getUrl() + "/indexes/?start=" + start + "&pageSize=" + pageSize; try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention))) { request.addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); RavenJArray json = (RavenJArray) request.readResponseJson(); return JsonConvert.deserializeObject(json, IndexDefinition.class, "definition"); } } @Override public List<TransformerDefinition> getTransformers(final int start, final int pageSize) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, List<TransformerDefinition>>() { @Override public List<TransformerDefinition> apply(OperationMetadata operationMetadata) { return directGetTransformers(operationMetadata, start, pageSize); } }); } protected List<TransformerDefinition> directGetTransformers(OperationMetadata operationMetadata, int start, int pageSize) { String operationUrl = operationMetadata.getUrl() + "/transformers?start=" + start + "&pageSize=" + pageSize; try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention))) { request.addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); RavenJToken result = request.readResponseJson(); RavenJArray json = ((RavenJArray) result); return JsonConvert.deserializeObject(json, TransformerDefinition.class, "definition"); } } @Override public void resetIndex(final String name) { executeWithReplication(HttpMethods.RESET, new Function1<OperationMetadata, Void>() { @Override public Void apply(OperationMetadata operationMetadata) { directResetIndex(name, operationMetadata); return null; } }); } protected void directResetIndex(String name, OperationMetadata operationMetadata) { try (HttpJsonRequest httpJsonRequest = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/indexes/" + name, HttpMethods.RESET, new RavenJObject(), operationMetadata.getCredentials(), convention))) { httpJsonRequest.addOperationHeaders(operationsHeaders); httpJsonRequest.addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); httpJsonRequest.readResponseJson(); } } @Override public String putIndex(String name, IndexDefinitionBuilder indexDef) { return putIndex(name, indexDef.toIndexDefinition(convention)); } @Override public String putIndex(String name, IndexDefinitionBuilder indexDef, boolean overwrite) { return putIndex(name, indexDef.toIndexDefinition(convention), overwrite); } @SuppressWarnings("boxing") @Override public boolean indexHasChanged(final String name, final IndexDefinition definition) { return executeWithReplication(HttpMethods.POST, new Function1<OperationMetadata, Boolean>() { @Override public Boolean apply(OperationMetadata input) { return directIndexHasChanged(name, definition, input); } }); } protected Boolean directIndexHasChanged(String name, IndexDefinition definition, OperationMetadata operationMetadata) { String requestUri = RavenUrlExtensions.indexes(operationMetadata.getUrl(), name) + "?op=hasChanged"; try (HttpJsonRequest webRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethods.POST, new RavenJObject(), operationMetadata.getCredentials(), convention))) { webRequest.addOperationHeaders(operationsHeaders); webRequest.addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); webRequest.write(JsonConvert.serializeObject(definition)); //we don't use default converters RavenJToken responseJson = webRequest.readResponseJson(); return responseJson.value(Boolean.class, "Changed"); } } @Override public String putIndex(final String name, final IndexDefinition definition) { return putIndex(name, definition, false); } @Override public String putIndex(final String name, final IndexDefinition definition, final boolean overwrite) { ensureIsNotNullOrEmpty(name, "name"); return executeWithReplication(HttpMethods.PUT, new Function1<OperationMetadata, String>() { @Override public String apply(OperationMetadata operationMetadata) { return directPutIndex(name, definition, overwrite, operationMetadata); } }); } @Override public String putTransformer(final String name, final TransformerDefinition indexDef) { ensureIsNotNullOrEmpty(name, "name"); return executeWithReplication(HttpMethods.PUT, new Function1<OperationMetadata, String>() { @Override public String apply(OperationMetadata operationMetadata) { return directPutTransformer(name, operationMetadata, indexDef); } }); } public String directPutIndex(String name, IndexDefinition definition, boolean overwrite, OperationMetadata operationMetadata) { String requestUri = operationMetadata.getUrl() + "/indexes/" + UrlUtils.escapeUriString(name) + "?definition=yes"; try (HttpJsonRequest webRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethods.HEAD, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { try { // If the index doesn't exist this will throw a NotFound exception and continue with a PUT request webRequest.executeRequest(); if (!overwrite) { throw new IllegalStateException("Cannot put index: " + name + ", index already exists"); } } catch (ErrorResponseException e) { if (e.getStatusCode() != HttpStatus.SC_NOT_FOUND) { throw e; } } } try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, requestUri, HttpMethods.PUT, new RavenJObject(), operationMetadata.getCredentials(), convention).addOperationHeaders(operationsHeaders))) { ErrorResponseException responseException; try { request.write(JsonConvert.serializeObject(definition)); //we don't use default converters RavenJToken responseJson = request.readResponseJson(); return responseJson.value(String.class, "Index"); } catch (ErrorResponseException e) { if (e.getStatusCode() != HttpStatus.SC_BAD_REQUEST) { throw e; } responseException = e; } IndexErrorObjectProto error = ExceptionExtensions .tryReadErrorResponseObject(IndexErrorObjectProto.class, responseException); if (error == null) { throw responseException; } IndexCompilationException compilationException = new IndexCompilationException(error.getMessage()); compilationException.setIndexDefinitionProperty(error.getIndexDefinitionProperty()); compilationException.setProblematicText(error.getProblematicText()); throw compilationException; } } public static class IndexErrorObjectProto { private String error; private String message; private String indexDefinitionProperty; private String problematicText; public String getError() { return error; } public void setError(String error) { this.error = error; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getIndexDefinitionProperty() { return indexDefinitionProperty; } public void setIndexDefinitionProperty(String indexDefinitionProperty) { this.indexDefinitionProperty = indexDefinitionProperty; } public String getProblematicText() { return problematicText; } public void setProblematicText(String problematicText) { this.problematicText = problematicText; } } public String directPutTransformer(String name, OperationMetadata operationMetadata, TransformerDefinition definition) { String requestUri = operationMetadata.getUrl() + "/transformers/" + name; try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, requestUri, HttpMethods.PUT, new RavenJObject(), operationMetadata.getCredentials(), convention).addOperationHeaders(operationsHeaders))) { ErrorResponseException responseException; try { request.write(JsonConvert.serializeObject(definition)); RavenJObject responseJson = (RavenJObject) request.readResponseJson(); return responseJson.value(String.class, "Transformer"); } catch (BadRequestException e) { throw new TransformCompilationException(e); } catch (ErrorResponseException e) { if (e.getStatusCode() != HttpStatus.SC_BAD_REQUEST) { throw e; } responseException = e; } ErrorObjectProtoTransformer error = ExceptionExtensions .tryReadErrorResponseObject(ErrorObjectProtoTransformer.class, responseException); if (error == null) { throw responseException; } throw new TransformCompilationException(error.getMessage()); } } public static class ErrorObjectProtoTransformer { private String error; private String message; public String getError() { return error; } public void setError(String error) { this.error = error; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } @Override public void deleteIndex(final String name) { ensureIsNotNullOrEmpty(name, "name"); executeWithReplication(HttpMethods.DELETE, new Function1<OperationMetadata, Void>() { @Override public Void apply(OperationMetadata operationMetadata) { directDeleteIndex(name, operationMetadata); return null; } }); } protected void directDeleteIndex(String name, OperationMetadata operationMetadata) { try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, indexes(operationMetadata.getUrl(), name), HttpMethods.DELETE, null, operationMetadata.getCredentials(), convention))) { request.addOperationHeaders(operationsHeaders); request.addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); request.executeRequest(); } } @Override public Operation deleteByIndex(String indexName, IndexQuery queryToDelete) { return deleteByIndex(indexName, queryToDelete, null); } @Override public Operation deleteByIndex(final String indexName, final IndexQuery queryToDelete, final BulkOperationOptions options) { return executeWithReplication(HttpMethods.DELETE, new Function1<OperationMetadata, Operation>() { @Override public Operation apply(OperationMetadata operationMetadata) { return directDeleteByIndex(operationMetadata, indexName, queryToDelete, options); } }); } protected Operation directDeleteByIndex(OperationMetadata operationMetadata, String indexName, IndexQuery queryToDelete, BulkOperationOptions options) { BulkOperationOptions notNullOptions = (options != null) ? options : new BulkOperationOptions(); String path = queryToDelete.getIndexQueryUrl(operationMetadata.getUrl(), indexName, "bulk_docs") + "&allowStale=" + notNullOptions.isAllowStale() + "&details=" + notNullOptions.isRetrieveDetails(); if (notNullOptions.getMaxOpsPerSec() != null) { path += "&maxOpsPerSec=" + notNullOptions.getMaxOpsPerSec(); } if (notNullOptions.getStaleTimeout() != null) { path += "&staleTimeout=" + notNullOptions.getStaleTimeout(); } try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, HttpMethods.DELETE, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJToken jsonResponse; try { jsonResponse = request.readResponseJson(); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { throw new IllegalStateException("There is no index named: " + indexName); } throw e; } if (jsonResponse == null || jsonResponse.getType() != JTokenType.OBJECT) { return null; } RavenJToken opId = ((RavenJObject) jsonResponse).get("OperationId"); if (opId == null || opId.getType() != JTokenType.INTEGER) { return null; } return new Operation(this, opId.value(Long.TYPE)); } } @Override public void deleteTransformer(final String name) { ensureIsNotNullOrEmpty(name, "name"); executeWithReplication(HttpMethods.DELETE, new Function1<OperationMetadata, Void>() { @Override public Void apply(OperationMetadata operationMetadata) { directDeleteTransformer(name, operationMetadata); return null; } }); } protected void directDeleteTransformer(final String name, OperationMetadata operationMetadata) { try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/transformers/" + name, HttpMethods.DELETE, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders))) { request.addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); request.executeRequest(); } } @Override public RavenJObject patch(String key, PatchRequest[] patches) { return patch(key, patches, null); } @Override public RavenJObject patch(String key, PatchRequest[] patches, boolean ignoreMissing) { PatchCommandData command = new PatchCommandData(); command.setKey(key); command.setPatches(patches); BatchResult[] batchResults = batch(Arrays.<ICommandData>asList(command)); if (!ignoreMissing && batchResults[0].getPatchResult() != null && batchResults[0].getPatchResult() == PatchResult.DOCUMENT_DOES_NOT_EXISTS) { throw new DocumentDoesNotExistsException("Document with key " + key + " does not exist."); } return batchResults[0].getAdditionalData(); } @Override public RavenJObject patch(String key, ScriptedPatchRequest patch) { return patch(key, patch, null); } @Override public RavenJObject patch(String key, ScriptedPatchRequest patch, boolean ignoreMissing) { ScriptedPatchCommandData command = new ScriptedPatchCommandData(); command.setKey(key); command.setPatch(patch); BatchResult[] batchResults = batch(Arrays.<ICommandData>asList(command)); if (!ignoreMissing && batchResults[0].getPatchResult() != null && batchResults[0].getPatchResult() == PatchResult.DOCUMENT_DOES_NOT_EXISTS) { throw new DocumentDoesNotExistsException("Document with key " + key + " does not exist."); } return batchResults[0].getAdditionalData(); } @Override public RavenJObject patch(String key, PatchRequest[] patches, Etag etag) { PatchCommandData command = new PatchCommandData(); command.setKey(key); command.setPatches(patches); command.setEtag(etag); BatchResult[] batchResults = batch(Arrays.<ICommandData>asList(command)); return batchResults[0].getAdditionalData(); } @Override public RavenJObject patch(String key, PatchRequest[] patchesToExisting, PatchRequest[] patchesToDefault, RavenJObject defaultMetadata) { PatchCommandData command = new PatchCommandData(); command.setKey(key); command.setPatches(patchesToExisting); command.setPatchesIfMissing(patchesToDefault); command.setMetadata(defaultMetadata); BatchResult[] batchResults = batch(Arrays.<ICommandData>asList(command)); return batchResults[0].getAdditionalData(); } @Override public RavenJObject patch(String key, ScriptedPatchRequest patch, Etag etag) { ScriptedPatchCommandData command = new ScriptedPatchCommandData(); command.setKey(key); command.setPatch(patch); command.setEtag(etag); BatchResult[] batchResults = batch(Arrays.<ICommandData>asList(command)); return batchResults[0].getAdditionalData(); } @Override public RavenJObject patch(String key, ScriptedPatchRequest patchExisting, ScriptedPatchRequest patchDefault, RavenJObject defaultMetadata) { ScriptedPatchCommandData command = new ScriptedPatchCommandData(); command.setKey(key); command.setPatch(patchExisting); command.setPatchIfMissing(patchDefault); command.setMetadata(defaultMetadata); BatchResult[] batchResults = batch(Arrays.<ICommandData>asList(command)); return batchResults[0].getAdditionalData(); } @Override public PutResult put(final String key, final Etag etag, final RavenJObject document, final RavenJObject metadata) { return executeWithReplication(HttpMethods.PUT, new Function1<OperationMetadata, PutResult>() { @Override public PutResult apply(OperationMetadata operationMetadata) { return directPut(metadata, key, etag, document, operationMetadata); } }); } protected PutResult directPut(RavenJObject metadata, String key, Etag etag, RavenJObject document, OperationMetadata operationMetadata) { if (metadata == null) { metadata = new RavenJObject(); } HttpMethods method = StringUtils.isNotEmpty(key) ? HttpMethods.PUT : HttpMethods.POST; if (etag != null) { metadata.set(Constants.METADATA_ETAG_FIELD, new RavenJValue(etag.toString())); } if (key != null) { key = UrlUtils.escapeUriString(key); } String requestUrl = operationMetadata.getUrl() + "/docs/" + ((key != null) ? key : ""); try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUrl, method, metadata, operationMetadata.getCredentials(), convention).addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { ErrorResponseException responseException; try { jsonRequest.write(document.toString()); RavenJObject responseJson = (RavenJObject) jsonRequest.readResponseJson(); if (responseJson == null) { throw new IllegalStateException("Got null response from the server after doing a put on " + key + ", something is very wrong. Probably a garbled response."); } return new PutResult(responseJson.value(String.class, "Key"), responseJson.value(Etag.class, "ETag")); } catch (ErrorResponseException e) { if (e.getStatusCode() != HttpStatus.SC_CONFLICT) { throw e; } responseException = e; } throw fetchConcurrencyException(responseException); } } @Override public IDatabaseCommands forDatabase(String database) { if (Constants.SYSTEM_DATABASE.equals(database)) { return forSystemDatabase(); } String databaseUrl = MultiDatabase.getRootDatabaseUrl(url); databaseUrl = databaseUrl + "/databases/" + database; if (databaseUrl.equals(url)) { return this; } ServerClient client = new ServerClient(databaseUrl, convention, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, jsonRequestFactory, sessionId, replicationInformerGetter, database, conflictListeners, false); client.setOperationsHeaders(operationsHeaders); return client; } @Override public IDatabaseCommands forSystemDatabase() { String databaseUrl = MultiDatabase.getRootDatabaseUrl(url); if (databaseUrl.equals(url)) { return this; } ServerClient client = new ServerClient(databaseUrl, convention, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, jsonRequestFactory, sessionId, replicationInformerGetter, null, conflictListeners, false); client.setOperationsHeaders(operationsHeaders); return client; } @Override public Map<String, String> getOperationsHeaders() { return operationsHeaders; } @Override public void setOperationsHeaders(Map<String, String> operationsHeaders) { this.operationsHeaders = operationsHeaders; } @Override public IGlobalAdminDatabaseCommands getGlobalAdmin() { return new AdminServerClient(this); } @Override public IAdminDatabaseCommands getAdmin() { return new AdminServerClient(this); } @Override public JsonDocument get(final String key) { ensureIsNotNullOrEmpty(key, "key"); return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, JsonDocument>() { @Override public JsonDocument apply(OperationMetadata operationMetadata) { return directGet(operationMetadata, key); } }); } @Override public TransformerDefinition getTransformer(final String name) { ensureIsNotNullOrEmpty(name, "name"); return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, TransformerDefinition>() { @Override public TransformerDefinition apply(OperationMetadata operationMetadata) { return directGetTransformer(name, operationMetadata); } }); } protected TransformerDefinition directGetTransformer(final String transformerName, final OperationMetadata operationMetadata) { try { try (HttpJsonRequest httpJsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/transformers/" + transformerName, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJToken transformerDef = httpJsonRequest.readResponseJson(); RavenJObject value = transformerDef.value(RavenJObject.class, "Transformer"); return convention.createSerializer().deserialize(value.toString(), TransformerDefinition.class); } } catch (ErrorResponseException we) { if (we.getStatusCode() == HttpStatus.SC_NOT_FOUND) { return null; } throw we; } } @Override public IndexDefinition getIndex(final String name) { ensureIsNotNullOrEmpty(name, "name"); return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, IndexDefinition>() { @Override public IndexDefinition apply(OperationMetadata operationMetadata) { return directGetIndex(name, operationMetadata); } }); } protected IndexDefinition directGetIndex(String indexName, OperationMetadata operationMetadata) { try { try (HttpJsonRequest httpJsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/indexes/" + indexName + "?definition=yes", HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJToken indexDef = httpJsonRequest.readResponseJson(); RavenJObject value = indexDef.value(RavenJObject.class, "Index"); return convention.createSerializer().deserialize(value, IndexDefinition.class); } } catch (ErrorResponseException we) { if (we.getStatusCode() == HttpStatus.SC_NOT_FOUND) { return null; } throw we; } } public JsonDocument directGet(OperationMetadata operationMetadata, String key) { if (key.length() > 127) { MultiLoadResult multiLoadResult = directGet(new String[] { key }, operationMetadata, new String[0], null, new HashMap<String, RavenJToken>(), false); List<RavenJObject> results = multiLoadResult.getResults(); if (results.get(0) == null) { return null; } return SerializationHelper.ravenJObjectToJsonDocument(results.get(0)); } RavenJObject metadata = new RavenJObject(); String actualUrl = operationMetadata.getUrl() + "/docs?id=" + UrlUtils.escapeDataString(key); CreateHttpJsonRequestParams createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, actualUrl, HttpMethods.GET, metadata, operationMetadata.getCredentials(), convention); try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest(createHttpJsonRequestParams) .addOperationHeaders(operationsHeaders).addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { try { RavenJToken responseJson = request.readResponseJson(); String docKey = request.getResponseHeaders().get(Constants.DOCUMENT_ID_FIELD_NAME); if (docKey == null) { docKey = key; } docKey = UrlUtils.unescapeDataString(docKey); request.getResponseHeaders().remove(Constants.DOCUMENT_ID_FIELD_NAME); return SerializationHelper.deserializeJsonDocument(docKey, responseJson, request.getResponseHeaders(), request.getResponseStatusCode()); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { return null; } else if (e.getStatusCode() == HttpStatus.SC_CONFLICT) { return resolveConflict(e.getResponseString(), e.getEtag(), operationMetadata, key); } throw e; } } } private JsonDocument resolveConflict(String httpResponse, Etag etag, OperationMetadata operationMetadata, String key) { RavenJObject conflictsDoc = RavenJObject.parse(httpResponse); ConflictException result = tryResolveConflictOrCreateConcurrencyException(operationMetadata, key, conflictsDoc, etag); if (result != null) { throw result; } return directGet(operationMetadata, key); } @Override public MultiLoadResult get(final String[] ids, final String[] includes) { return get(ids, includes, null, null, false); } @Override public MultiLoadResult get(final String[] ids, final String[] includes, final String transformer) { return get(ids, includes, transformer, null, false); } @Override public MultiLoadResult get(final String[] ids, final String[] includes, final String transformer, final Map<String, RavenJToken> transformerParameters) { return get(ids, includes, transformer, transformerParameters, false); } @Override public MultiLoadResult get(final String[] ids, final String[] includes, final String transformer, final Map<String, RavenJToken> transformerParameters, final boolean metadataOnly) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, MultiLoadResult>() { @Override public MultiLoadResult apply(OperationMetadata operationMetadata) { return directGet(ids, operationMetadata, includes, transformer, transformerParameters != null ? transformerParameters : new HashMap<String, RavenJToken>(), metadataOnly); } }); } protected MultiLoadResult directGet(final String[] ids, final OperationMetadata operationMetadata, final String[] includes, final String transformer, final Map<String, RavenJToken> transformerParameters, final boolean metadataOnly) { String path = operationMetadata.getUrl() + "/queries/?"; if (metadataOnly) path += "metadata-only=true&"; if (includes != null && includes.length > 0) { List<String> tokens = new ArrayList<>(); for (String include : includes) { tokens.add("include=" + include); } path += "&" + StringUtils.join(tokens, "&"); } if (StringUtils.isNotEmpty(transformer)) { path += "&transformer=" + transformer; } if (transformerParameters != null) { for (Entry<String, RavenJToken> transformerParameter : transformerParameters.entrySet()) { path += String.format("&tp-%s=%s", transformerParameter.getKey(), transformerParameter.getValue()); } } RavenJObject metadata = new RavenJObject(); Set<String> uniqueIds = new LinkedHashSet<>(Arrays.asList(ids)); // if it is too big, we drop to POST (note that means that we can't use the HTTP cache any longer) // we are fine with that, requests to load that many items are probably going to be rare HttpJsonRequest request = null; try { int uniqueIdsSum = 0; for (String id : ids) { uniqueIdsSum += id.length(); } if (uniqueIdsSum < 1024) { for (String uniqueId : uniqueIds) { path += "&id=" + UrlUtils.escapeDataString(uniqueId); } request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, HttpMethods.GET, metadata, operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); } else { request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, HttpMethods.POST, metadata, operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); request.write(RavenJToken.fromObject(uniqueIds).toString()); } RavenJToken result = request.readResponseJson(); return completeMultiGet(operationMetadata, ids, includes, transformer, transformerParameters, result); } finally { if (request != null) { request.close(); } } } private MultiLoadResult completeMultiGet(final OperationMetadata operationMetadata, final String[] keys, final String[] includes, final String transformer, final Map<String, RavenJToken> transformerParameters, RavenJToken result) { ErrorResponseException responseException; try { HashSet<String> uniqueKeys = new HashSet<>(Arrays.asList(keys)); List<RavenJObject> results = new ArrayList<>(); for (RavenJToken token : result.value(RavenJArray.class, "Results")) { if (token instanceof RavenJObject) { results.add((RavenJObject) token); } } Map<String, RavenJObject> documents = new HashMap<>(); for (RavenJObject doc : results) { if (doc.containsKey("@metadata") && doc.get("@metadata").value(String.class, "@id") != null) { documents.put(doc.get("@metadata").value(String.class, "@id"), doc); } } if (results.size() >= uniqueKeys.size()) { for (int i = 0; i < uniqueKeys.size(); i++) { String key = keys[i]; if (documents.containsKey(key)) { continue; } documents.put(key, results.get(i)); } } MultiLoadResult multiLoadResult = new MultiLoadResult(); List<RavenJObject> includesList = new ArrayList<>(); for (RavenJToken token : result.value(RavenJArray.class, "Includes")) { includesList.add((RavenJObject) token); } multiLoadResult.setIncludes(includesList); List<RavenJObject> resultsList = new ArrayList<>(); for (String key : keys) { if (documents.containsKey(key)) { resultsList.add(documents.get(key)); } else { resultsList.add(null); } } multiLoadResult.setResults(resultsList); List<RavenJObject> docResults = new ArrayList<>(); docResults.addAll(resultsList); docResults.addAll(includesList); return retryOperationBecauseOfConflict(operationMetadata, docResults, multiLoadResult, new Function0<MultiLoadResult>() { @Override public MultiLoadResult apply() { return directGet(keys, operationMetadata, includes, transformer, transformerParameters, false); } }, null); } catch (ErrorResponseException e) { if (e.getStatusCode() != HttpStatus.SC_CONFLICT) { throw e; } responseException = e; } throw fetchConcurrencyException(responseException); } @Override public List<JsonDocument> getDocuments(int start, int pageSize) { return getDocuments(start, pageSize, false); } @Override public List<JsonDocument> getDocuments(final int start, final int pageSize, final boolean metadataOnly) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, List<JsonDocument>>() { @Override public List<JsonDocument> apply(OperationMetadata operationMetadata) { String requestUri = operationMetadata.getUrl() + "/docs?start=" + start + "&pageSize=" + pageSize; if (metadataOnly) { requestUri += "&metadata-only=true"; } try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(ServerClient.this, requestUri, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders))) { RavenJToken responseJson = request.readResponseJson(); return SerializationHelper.ravenJObjectsToJsonDocuments(responseJson); } } }); } //TODO: getDocuments from etag @Override public Operation updateByIndex(String indexName, IndexQuery queryToUpdate, PatchRequest[] patchRequests) { return updateByIndex(indexName, queryToUpdate, patchRequests, null); } @Override public Operation updateByIndex(String indexName, IndexQuery queryToUpdate, ScriptedPatchRequest patch) { return updateByIndex(indexName, queryToUpdate, patch, null); } @Override public Operation updateByIndex(String indexName, IndexQuery queryToUpdate, PatchRequest[] patchRequests, BulkOperationOptions options) { RavenJArray array = new RavenJArray(); for (PatchRequest request : patchRequests) { array.add(request.toJson()); } String requestData = array.toString(); BulkOperationOptions notNullOptions = (options != null) ? options : new BulkOperationOptions(); return updateByIndexImpl(indexName, queryToUpdate, notNullOptions, requestData, HttpMethods.PATCH); } @Override public Operation updateByIndex(String indexName, IndexQuery queryToUpdate, ScriptedPatchRequest patch, BulkOperationOptions options) { String requestData = RavenJObject.fromObject(patch).toString(); BulkOperationOptions notNullOptions = (options != null) ? options : new BulkOperationOptions(); return updateByIndexImpl(indexName, queryToUpdate, notNullOptions, requestData, HttpMethods.EVAL); } @Override public MultiLoadResult moreLikeThis(MoreLikeThisQuery query) { final String requestUrl = query.getRequestUri(); ensureIsNotNullOrEmpty(requestUrl, "url"); RavenJToken result = executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, RavenJToken>() { @Override public RavenJToken apply(OperationMetadata operationMetadata) { RavenJObject metadata = new RavenJObject(); try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(ServerClient.this, operationMetadata.getUrl() + requestUrl, HttpMethods.GET, metadata, operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders))) { return request.readResponseJson(); } } }); MultiLoadResult multiLoadResult = new MultiLoadResult(); multiLoadResult.setIncludes( new ArrayList<>(result.value(RavenJArray.class, "Includes").values(RavenJObject.class))); multiLoadResult .setResults(new ArrayList<>(result.value(RavenJArray.class, "Results").values(RavenJObject.class))); return multiLoadResult; } @Override public Long nextIdentityFor(final String name) { return executeWithReplication(HttpMethods.POST, new Function1<OperationMetadata, Long>() { @Override public Long apply(OperationMetadata operationMetadata) { return directNextIdentityFor(name, operationMetadata); } }); } protected Long directNextIdentityFor(String name, OperationMetadata operationMetadata) { try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/identity/next?name=" + UrlUtils.escapeDataString(name), HttpMethods.POST, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders))) { RavenJToken ravenJToken = jsonRequest.readResponseJson(); return ravenJToken.value(Long.class, "Value"); } } @SuppressWarnings("boxing") @Override public long seedIdentityFor(final String name, final long value) { return executeWithReplication(HttpMethods.POST, new Function1<OperationMetadata, Long>() { @Override public Long apply(OperationMetadata operationMetadata) { return directSeedIdentityFor(operationMetadata, name, value); } }); } @SuppressWarnings("boxing") long directSeedIdentityFor(OperationMetadata operationMetadata, String name, long value) { try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/identity/seed?name=" + UrlUtils.escapeDataString(name) + "&value=" + value, HttpMethods.POST, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders))) { RavenJToken readResponseJson = request.readResponseJson(); return readResponseJson.value(Long.TYPE, "Value"); } } private Operation updateByIndexImpl(final String indexName, final IndexQuery queryToUpdate, final BulkOperationOptions options, final String requestData, final HttpMethods method) { return executeWithReplication(method, new Function1<OperationMetadata, Operation>() { @Override public Operation apply(OperationMetadata operationMetadata) { return directUpdateByIndexImpl(operationMetadata, indexName, queryToUpdate, options, requestData, method); } }); } protected Operation directUpdateByIndexImpl(OperationMetadata operationMetadata, String indexName, IndexQuery queryToUpdate, BulkOperationOptions options, String requestData, HttpMethods method) { BulkOperationOptions notNullOptions = (options != null) ? options : new BulkOperationOptions(); String path = queryToUpdate.getIndexQueryUrl(operationMetadata.getUrl(), indexName, "bulk_docs") + "&allowStale=" + notNullOptions.isAllowStale() + "&maxOpsPerSec=" + notNullOptions.getMaxOpsPerSec() + "&details=" + notNullOptions.isRetrieveDetails(); if (notNullOptions.getStaleTimeout() != null) { path += "&staleTimeout=" + notNullOptions.getStaleTimeout(); } try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, method, new RavenJObject(), operationMetadata.getCredentials(), convention).addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJToken jsonResponse; try { request.write(requestData); jsonResponse = request.readResponseJson(); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { throw new IllegalStateException("There is no index named: " + indexName); } throw e; } return new Operation(this, jsonResponse.value(Long.TYPE, "OperationId")); } } @Override public FacetResults getFacets(final String index, final IndexQuery query, final String facetSetupDoc) { return getFacets(index, query, facetSetupDoc, 0, null); } @Override public FacetResults getFacets(final String index, final IndexQuery query, final String facetSetupDoc, final int start) { return getFacets(index, query, facetSetupDoc, start, null); } @Override public FacetResults getFacets(final String index, final IndexQuery query, final String facetSetupDoc, final int start, final Integer pageSize) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, FacetResults>() { @Override public FacetResults apply(OperationMetadata operationMetadata) { return directGetFacets(operationMetadata, index, query, facetSetupDoc, start, pageSize); } }); } protected FacetResults directGetFacets(OperationMetadata operationMetadata, String index, IndexQuery query, String facetSetupDoc, int start, Integer pageSize) { String requestUri = operationMetadata.getUrl() + String.format("/facets/%s?facetDoc=%s&%s&facetStart=%d&facetPageSize=%s", UrlUtils.escapeUriString(index), UrlUtils.escapeDataString(facetSetupDoc), query.getMinimalQueryString(), start, pageSize != null ? pageSize : ""); try (final HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { CachedRequestOp cachedRequestDetails = jsonRequestFactory.configureCaching(requestUri, new Action2<String, String>() { @Override public void apply(String key, String val) { request.addOperationHeader(key, val); } }); request.setCachedRequestDetails(cachedRequestDetails.getCachedRequest()); request.setSkipServerCheck(cachedRequestDetails.isSkipServerCheck()); RavenJObject json = (RavenJObject) request.readResponseJson(); return convention.createSerializer().deserialize(json.toString(), FacetResults.class); } } @Override public FacetResults[] getMultiFacets(final FacetQuery[] facetedQueries) { GetRequest[] multiGetRequestItems = new GetRequest[facetedQueries.length]; for (int i = 0; i < facetedQueries.length; i++) { FacetQuery x = facetedQueries[i]; String addition = x.getFacetSetupDoc() != null ? "facetDoc=" + x.getFacetSetupDoc() : "facets=" + UrlUtils.escapeDataString(JsonConvert.serializeObject(x.getFacets())); GetRequest request = new GetRequest(); request.setUrl("/facets/" + x.getIndexName()); request.setQuery(String.format("%s&facetSTart=%d&facetPageSize=%d&%d", x.getQuery().getQueryString(), x.getQuery().getStart(), x.getQuery().getPageSize(), addition)); multiGetRequestItems[i] = request; } GetResponse[] results = multiGet(multiGetRequestItems); JsonSerializer jsonSerializer = convention.createSerializer(); FacetResults[] facetResults = new FacetResults[results.length]; for (int facetResultCounter = 0; facetResultCounter < facetResults.length; facetResultCounter++) { GetResponse curFacetDoc = results[facetResultCounter]; facetResults[facetResultCounter] = jsonSerializer.deserialize(curFacetDoc.getResult(), FacetResults.class); } return facetResults; } @Override public FacetResults getFacets(final String index, final IndexQuery query, final List<Facet> facets) { return getFacets(index, query, facets, 0, null); } @Override public FacetResults getFacets(final String index, final IndexQuery query, final List<Facet> facets, final int start) { return getFacets(index, query, facets, start, null); } @Override public FacetResults getFacets(final String index, final IndexQuery query, final List<Facet> facets, final int start, final Integer pageSize) { final String facetsJson = JsonConvert.serializeObject(facets); final HttpMethods method = facetsJson.length() > 1024 ? HttpMethods.POST : HttpMethods.GET; return executeWithReplication(method, new Function1<OperationMetadata, FacetResults>() { @Override public FacetResults apply(OperationMetadata operationMetadata) { return directGetFacets(operationMetadata, index, query, facetsJson, start, pageSize, method); } }); } //TODO: trim and use get protected FacetResults directGetFacets(OperationMetadata operationMetadata, String index, IndexQuery query, String facetsJson, int start, Integer pageSize, HttpMethods method) { String requestUri = operationMetadata.getUrl() + String.format("/facets/%s?%s&facetStart=%d&facetPageSize=%s", UrlUtils.escapeUriString(index), query.getQueryString(), start, (pageSize != null) ? pageSize.toString() : ""); if (method == HttpMethods.GET) { requestUri += "&facets=" + UrlUtils.escapeDataString(facetsJson); } try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, method, new RavenJObject(), operationMetadata.getCredentials(), convention).addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { if (method != HttpMethods.GET) request.write(facetsJson); RavenJObject json = (RavenJObject) request.readResponseJson(); return convention.createSerializer().deserialize(json, FacetResults.class); } } //TODO: get logs //TODO: get license status @Override public BuildNumber getBuildNumber() { try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, url + "/build/version", HttpMethods.GET, new RavenJObject(), credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention))) { request.addOperationHeaders(operationsHeaders); RavenJToken result = request.readResponseJson(); return convention.createSerializer().deserialize(result, BuildNumber.class); } } @Override public IndexMergeResults getIndexMergeSuggestions() throws IOException { try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest(new CreateHttpJsonRequestParams( this, url + "/debug/suggest-index-merge", HttpMethods.GET, new RavenJObject(), credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention))) { request.addOperationHeaders(operationsHeaders); RavenJToken result = request.readResponseJson(); return convention.createSerializer().deserialize(result, IndexMergeResults.class); } } @Override public List<JsonDocument> startsWith(final String keyPrefix, final String matches, final int start, final int pageSize) { return startsWith(keyPrefix, matches, start, pageSize, false); } @Override public List<JsonDocument> startsWith(final String keyPrefix, final String matches, final int start, final int pageSize, final boolean metadataOnly) { return startsWith(keyPrefix, matches, start, pageSize, metadataOnly, null, null, null, null, null); } @Override public List<JsonDocument> startsWith(final String keyPrefix, final String matches, final int start, final int pageSize, final boolean metadataOnly, final String exclude) { return startsWith(keyPrefix, matches, start, pageSize, metadataOnly, exclude, null, null, null, null); } @Override public List<JsonDocument> startsWith(final String keyPrefix, final String matches, final int start, final int pageSize, final boolean metadataOnly, final String exclude, final RavenPagingInformation pagingInformation) { return startsWith(keyPrefix, matches, start, pageSize, metadataOnly, exclude, pagingInformation, null, null, null); } @Override public List<JsonDocument> startsWith(final String keyPrefix, final String matches, final int start, final int pageSize, final boolean metadataOnly, final String exclude, final RavenPagingInformation pagingInformation, final String transformer, final Map<String, RavenJToken> transformerParameters) { return startsWith(keyPrefix, matches, start, pageSize, metadataOnly, exclude, pagingInformation, transformer, transformerParameters, null); } @Override public List<JsonDocument> startsWith(final String keyPrefix, final String matches, final int start, final int pageSize, final boolean metadataOnly, final String exclude, final RavenPagingInformation pagingInformation, final String transformer, final Map<String, RavenJToken> transformerParameters, final String skipAfter) { ensureIsNotNullOrEmpty(keyPrefix, "keyPrefix"); return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, List<JsonDocument>>() { @Override public List<JsonDocument> apply(OperationMetadata operationMetadata) { return directStartsWith(operationMetadata, keyPrefix, matches, start, pageSize, metadataOnly, exclude, pagingInformation, transformer, transformerParameters, skipAfter); } }); } @SuppressWarnings("null") protected List<JsonDocument> directStartsWith(final OperationMetadata operationMetadata, final String keyPrefix, final String matches, final int start, final int pageSize, final boolean metadataOnly, final String exclude, final RavenPagingInformation pagingInformation, final String transformer, final Map<String, RavenJToken> transformerParameters, final String skipAfter) { RavenJObject metadata = new RavenJObject(); int actualStart = start; boolean nextPage = pagingInformation != null && pagingInformation.isForPreviousPage(start, pageSize); if (nextPage) { actualStart = pagingInformation.getNextPageStart(); } String actualUrl = operationMetadata.getUrl() + String.format( "/docs?startsWith=%s&matches=%s&exclude=%s&start=%d&pageSize=%d", UrlUtils.escapeDataString(keyPrefix), UrlUtils.escapeDataString(StringUtils.trimToEmpty(matches)), UrlUtils.escapeDataString(StringUtils.trimToEmpty(exclude)), actualStart, pageSize); if (metadataOnly) { actualUrl += "&metadata-only=true"; } if (StringUtils.isNotEmpty(skipAfter)) { actualUrl += "&skipAfter=" + UrlUtils.escapeDataString(skipAfter); } if (StringUtils.isNotEmpty(transformer)) { actualUrl += "&transformer=" + transformer; if (transformerParameters != null) { for (Map.Entry<String, RavenJToken> entry : transformerParameters.entrySet()) { actualUrl += String.format("&tp-%s=%s", entry.getKey(), entry.getValue()); } } } if (nextPage) { actualUrl += "&next-page=true"; } try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, actualUrl, HttpMethods.GET, metadata, operationMetadata.getCredentials(), convention).addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJArray result = (RavenJArray) request.readResponseJson(); if (pagingInformation != null) { try { int nextPageStart = Integer .parseInt(request.getResponseHeaders().get(Constants.NEXT_PAGE_START)); pagingInformation.fill(start, pageSize, nextPageStart); } catch (NumberFormatException e) { //ignore } } List<RavenJObject> docResults = new ArrayList<RavenJObject>(); for (RavenJToken token : result) { if (token instanceof RavenJObject) { docResults.add((RavenJObject) token.cloneToken()); } } final int actualStartFinal = actualStart; List<JsonDocument> startsWithResults = SerializationHelper.ravenJObjectsToJsonDocuments(docResults); return retryOperationBecauseOfConflict(operationMetadata, docResults, startsWithResults, new Function0<List<JsonDocument>>() { @Override public List<JsonDocument> apply() { return startsWith(keyPrefix, matches, actualStartFinal, pageSize, metadataOnly, exclude, pagingInformation, transformer, transformerParameters, skipAfter); } }, new Function1<String, ConflictException>() { @Override public ConflictException apply(String conflictedResultId) { ConflictException conflictException = new ConflictException( "Conflict detected on " + conflictedResultId.substring(0, conflictedResultId.indexOf("/conflicts/")) + ", conflict must be resolved before the document will be accessible", true); conflictException.setConflictedVersionIds(new String[] { conflictedResultId }); return conflictException; } }); } } @Override public GetResponse[] multiGet(final GetRequest[] requests) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, GetResponse[]>() { @Override public GetResponse[] apply(OperationMetadata operationMetadata) { return directMultiGet(operationMetadata, requests); } }); } protected GetResponse[] directMultiGet(final OperationMetadata operationMetadata, GetRequest[] requests) { MultiGetOperation multiGetOperation = new MultiGetOperation(this, convention, operationMetadata.getUrl(), requests); try (HttpJsonRequest httpJsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, multiGetOperation.getRequestUri(), HttpMethods.POST, new RavenJObject(), operationMetadata.getCredentials(), convention))) { GetRequest[] requestsForServer = multiGetOperation.preparingForCachingRequest(jsonRequestFactory); String postedData = JsonConvert.serializeObject(requestsForServer); if (multiGetOperation.canFullyCache(jsonRequestFactory, httpJsonRequest, postedData)) { return multiGetOperation.handleCachingResponse(new GetResponse[requests.length], jsonRequestFactory); } httpJsonRequest.write(postedData); RavenJArray results = (RavenJArray) httpJsonRequest.readResponseJson(); GetResponse[] responses = convention.createSerializer().deserialize(results, GetResponse[].class); multiGetOperation.tryResolveConflictOrCreateConcurrencyException(responses, new Function3<String, RavenJObject, Etag, ConflictException>() { @Override public ConflictException apply(String key, RavenJObject conflictsDoc, Etag etag) { return tryResolveConflictOrCreateConcurrencyException(operationMetadata, key, conflictsDoc, etag); } }); return multiGetOperation.handleCachingResponse(responses, jsonRequestFactory); } } @Override public QueryResult query(String index, IndexQuery query) { return query(index, query, null, false, false); } @Override public QueryResult query(String index, IndexQuery query, String[] includes) { return query(index, query, includes, false, false); } @Override public QueryResult query(String index, IndexQuery query, String[] includes, boolean metadataOnly) { return query(index, query, includes, metadataOnly, false); } @Override public QueryResult query(final String index, final IndexQuery query, final String[] includes, final boolean metadataOnly, final boolean indexEntriesOnly) { ensureIsNotNullOrEmpty(index, "index"); final HttpMethods method = query.getQuery() == null || query.getQuery().length() <= convention.getMaxLengthOfQueryUsingGetUrl() ? HttpMethods.GET : HttpMethods.POST; if (HttpMethods.POST.equals(method)) { return executeWithReplication(method, new Function1<OperationMetadata, QueryResult>() { @Override public QueryResult apply(OperationMetadata operationMetadata) { return directQueryAsPost(index, query, operationMetadata, includes); } }); } return executeWithReplication(method, new Function1<OperationMetadata, QueryResult>() { @Override public QueryResult apply(OperationMetadata operationMetadata) { return directQueryAsGet(index, query, operationMetadata, includes, metadataOnly, indexEntriesOnly); } }); } protected QueryResult directQueryAsGet(final String index, final IndexQuery query, final OperationMetadata operationMetadata, final String[] includes, final boolean metadataOnly, final boolean includeEntries) { String path = query.getIndexQueryUrl(operationMetadata.getUrl(), index, "indexes", true, true); if (metadataOnly) path += "&metadata-only=true"; if (includeEntries) path += "&debug=entries"; if (includes != null && includes.length > 0) { for (String include : includes) { path += "&include=" + include; } } try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention) .setAvoidCachingRequest(query.isDisableCaching()) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJObject json = (RavenJObject) request.readResponseJson(); ErrorResponseException responseException; try { if (json == null) throw new IllegalStateException( "Got empty response from the server for the following request: " + request.getUrl()); QueryResult queryResult = SerializationHelper.toQueryResult(json, HttpExtensions.getEtagHeader(request), request.getResponseHeaders().get("Temp-Request-Time"), request.getSize()); List<RavenJObject> docsResults = new ArrayList<>(); docsResults.addAll(queryResult.getResults()); docsResults.addAll(queryResult.getIncludes()); return retryOperationBecauseOfConflict(operationMetadata, docsResults, queryResult, new Function0<QueryResult>() { @Override public QueryResult apply() { return directQueryAsGet(index, query, operationMetadata, includes, metadataOnly, includeEntries); } }, null); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { String text = e.getResponseString(); if (text.contains("maxQueryString")) throw new IllegalStateException(text, e); throw new IllegalStateException("There is no index named: " + index, e); } responseException = e; } if (handleException(responseException)) return null; throw responseException; } } protected QueryResult directQueryAsPost(final String index, final IndexQuery query, final OperationMetadata operationMetadata, final String[] includes) { StringBuilder stringBuilder = new StringBuilder(); query.appendQueryString(stringBuilder); if (includes != null && includes.length > 0) { for (String include : includes) { stringBuilder.append("&include=").append(include); } } GetRequest getRequest = new GetRequest(); getRequest.setQuery(stringBuilder.toString()); getRequest.setUrl("/indexes/" + index); try { GetResponse[] x = multiGet(new GetRequest[] { getRequest }); GetResponse getResponse = x[0]; RavenJObject json = (RavenJObject) getResponse.getResult(); return SerializationHelper.toQueryResult(json, HttpExtensions.getEtagHeader(getResponse), getResponse.getHeaders().get("Temp-Request-Time"), -1); } catch (ErrorResponseException errorResponseException) { if (errorResponseException.getStatusCode() == HttpStatus.SC_NOT_FOUND) { String text = errorResponseException.getResponseString(); if (text.contains("maxQueryString")) throw new IllegalStateException(text, errorResponseException); throw new IllegalStateException("There is no index named: " + index, errorResponseException); } if (handleException(errorResponseException)) return null; throw errorResponseException; } } private boolean handleException(ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_INTERNAL_SERVER_ERROR) { String content = e.getResponseString(); RavenJObject json = RavenJObject.fromObject(content); ServerRequestError error = convention.createSerializer().deserialize(json, ServerRequestError.class); throw new ErrorResponseException(e, error.getError()); } return false; } @Override public SuggestionQueryResult suggest(final String index, final SuggestionQuery suggestionQuery) { if (suggestionQuery == null) { throw new IllegalArgumentException("suggestionQuery"); } return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, SuggestionQueryResult>() { @Override public SuggestionQueryResult apply(OperationMetadata operationMetadata) { return directSuggest(index, suggestionQuery, operationMetadata); } }); } protected SuggestionQueryResult directSuggest(String index, SuggestionQuery suggestionQuery, OperationMetadata operationMetadata) { String requestUri = operationMetadata.getUrl() + String.format("/suggest/%s?term=%s&field=%s&max=%d&popularity=%s", UrlUtils.escapeUriString(index), UrlUtils.escapeDataString(suggestionQuery.getTerm()), UrlUtils.escapeDataString(suggestionQuery.getField()), suggestionQuery.getMaxSuggestions(), suggestionQuery.isPopularity()); if (suggestionQuery.getDistance() != null) { requestUri += "&distance=" + UrlUtils.escapeDataString(SharpEnum.value(suggestionQuery.getDistance())); } if (suggestionQuery.getAccuracy() != null) { requestUri += "&accuracy=" + NumberUtil .trimZeros(String.format(Constants.getDefaultLocale(), "%.4f", suggestionQuery.getAccuracy())); } try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJObject json = (RavenJObject) request.readResponseJson(); List<String> suggestions = new ArrayList<>(); SuggestionQueryResult result = new SuggestionQueryResult(); RavenJArray array = (RavenJArray) json.get("Suggestions"); for (RavenJToken token : array) { suggestions.add(token.value(String.class)); } result.setSuggestions(suggestions.toArray(new String[0])); return result; } } @Override public BatchResult[] batch(final List<ICommandData> commandDatas) { return executeWithReplication(HttpMethods.POST, new Function1<OperationMetadata, BatchResult[]>() { @Override public BatchResult[] apply(OperationMetadata operationMetadata) { return directBatch(commandDatas, operationMetadata); } }); } protected BatchResult[] directBatch(List<ICommandData> commandDatas, OperationMetadata operationMetadata) { RavenJObject metadata = new RavenJObject(); try (HttpJsonRequest req = jsonRequestFactory .createHttpJsonRequest( new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/bulk_docs", HttpMethods.POST, metadata, operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJArray jArray = new RavenJArray(); for (ICommandData command : commandDatas) { jArray.add(command.toJson()); } ErrorResponseException responseException; try { req.write(jArray.toString()); RavenJArray response = (RavenJArray) req.readResponseJson(); if (response == null) { throw new IllegalStateException( "Got null response from the server after doing a batch, something is very wrong. Probably a garbled response. Posted: " + jArray); } return JsonConvert.deserializeObject(BatchResult[].class, response.toString()); } catch (ErrorResponseException e) { if (e.getStatusCode() != HttpStatus.SC_CONFLICT) { throw e; } responseException = e; } throw fetchConcurrencyException(responseException); } } protected ConcurrencyException fetchConcurrencyException(ErrorResponseException e) { String text = e.getResponseString(); RavenJObject ravenJToken = RavenJObject.parse(text); return new ConcurrencyException(ravenJToken.value(Etag.class, "ExpectedETag"), ravenJToken.value(Etag.class, "ActualETag"), ravenJToken.value(String.class, "Error"), e); } private static void ensureIsNotNullOrEmpty(String key, String argName) { if (key == null || "".equals(key)) { throw new IllegalArgumentException("Key cannot be null or empty " + argName); } } @Override @Deprecated public AttachmentInformation[] getAttachments(final int start, final Etag startEtag, final int pageSize) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, AttachmentInformation[]>() { @Override public AttachmentInformation[] apply(OperationMetadata operationMetadata) { return directGetAttachments(start, startEtag, pageSize, operationMetadata); } }); } @Deprecated protected AttachmentInformation[] directGetAttachments(int start, Etag startEtag, int pageSize, OperationMetadata operationMetadata) { try (HttpJsonRequest webRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/static/?pageSize=" + pageSize + "&etag=" + startEtag + "&start=" + start, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJArray json = (RavenJArray) webRequest.readResponseJson(); return convention.createSerializer().deserialize(json, AttachmentInformation[].class); } } @Override @Deprecated public void putAttachment(final String key, final Etag etag, final InputStream data, final RavenJObject metadata) { executeWithReplication(HttpMethods.PUT, new Function1<OperationMetadata, Void>() { @Override public Void apply(OperationMetadata operationMetadata) { directPutAttachment(key, metadata, etag, data, operationMetadata); return null; } }); } @Deprecated protected void directPutAttachment(String key, RavenJObject metadata, Etag etag, InputStream data, OperationMetadata operationMetadata) { if (metadata == null) { metadata = new RavenJObject(); } if (etag != null) { metadata.set(Constants.METADATA_ETAG_FIELD, new RavenJValue(etag.toString())); } try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest( new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/static/" + key, HttpMethods.PUT, metadata, operationMetadata.getCredentials(), convention)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { jsonRequest.write(data); } } @Override @Deprecated public Attachment getAttachment(final String key) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, Attachment>() { @Override public Attachment apply(OperationMetadata operationMetadata) { return directGetAttachment(HttpMethods.GET, key, operationMetadata); } }); } @Override @Deprecated public Attachment headAttachment(final String key) { return executeWithReplication(HttpMethods.HEAD, new Function1<OperationMetadata, Attachment>() { @Override public Attachment apply(OperationMetadata operationMetadata) { return directGetAttachment(HttpMethods.HEAD, key, operationMetadata); } }); } @Deprecated protected Attachment directGetAttachment(HttpMethods method, String key, OperationMetadata operationMetadata) { RavenJObject metadata = new RavenJObject(); CreateHttpJsonRequestParams createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/static/" + key, method, metadata, operationMetadata.getCredentials(), convention); try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest(createHttpJsonRequestParams) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { ErrorResponseException responseException; try { byte[] result = request.readResponseBytes(); handleReplicationStatusChanges(request.getResponseHeaders(), url, operationMetadata.getUrl()); return new Attachment(HttpMethods.GET.equals(method), result, result.length, MetadataExtensions.filterHeadersAttachment(request.getResponseHeaders()), HttpExtensions.getEtagHeader(request), null); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) return null; if (e.getStatusCode() != HttpStatus.SC_CONFLICT) throw e; responseException = e; } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } try (CloseableHttpResponse response = responseException.getResponse()) { String stream = BomUtils.removeUTF8BOM(IOUtils.toString(response.getEntity().getContent()).trim()); List<String> conflictedIds; if (HttpMethods.GET.equals(method)) { RavenJObject conflictsDoc = RavenJObject.parse(stream); conflictedIds = conflictsDoc.value(RavenJArray.class, "Conflicts").values(String.class); } else { conflictedIds = Arrays.asList("Cannot get conflict ids in HEAD requesT"); } ConflictException ex = new ConflictException("Conflict detected on " + key + ", conflict must be resolved before the attachment will be accessible", true); ex.setConflictedVersionIds(conflictedIds.toArray(new String[0])); ex.setEtag(responseException.getEtag()); throw ex; } catch (IOException e) { throw new IllegalStateException(e.getMessage(), e); } } } @Override @Deprecated public void deleteAttachment(final String key, final Etag etag) { executeWithReplication(HttpMethods.DELETE, new Function1<OperationMetadata, Void>() { @Override public Void apply(OperationMetadata operationMetadata) { directDeleteAttachment(key, etag, operationMetadata); return null; } }); } @Deprecated protected void directDeleteAttachment(String key, Etag etag, OperationMetadata operationMetadata) { RavenJObject metadata = new RavenJObject(); if (etag != null) { metadata.add(Constants.METADATA_ETAG_FIELD, RavenJToken.fromObject(etag.toString())); } try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest( new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/static/" + key, HttpMethods.DELETE, metadata, operationMetadata.getCredentials(), convention)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { jsonRequest.executeRequest(); } } @Override public CleanCloseable disableAllCaching() { return jsonRequestFactory.disableAllCaching(); } @Override public List<String> getTerms(final String index, final String field, final String fromValue, final int pageSize) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, List<String>>() { @Override public List<String> apply(OperationMetadata operationMetadata) { return directGetTerms(operationMetadata, index, field, fromValue, pageSize); } }); } protected List<String> directGetTerms(OperationMetadata operationMetadata, String index, String field, String fromValue, int pageSize) { String requestUri = operationMetadata.getUrl() + String.format("/terms/%s?field=%s&pageSize=%d&fromValue=%s", UrlUtils.escapeUriString(index), UrlUtils.escapeDataString(field), pageSize, UrlUtils.escapeDataString(fromValue != null ? fromValue : "")); try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethods.GET, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { return request.readResponseJson().values(String.class); } } @Override public ProfilingInformation getProfilingInformation() { return profilingInformation; } public void addFailoverStatusChanged(EventHandler<FailoverStatusChangedEventArgs> event) { replicationInformer.addFailoverStatusChanged(event); } public void removeFailoverStatusChanged(EventHandler<FailoverStatusChangedEventArgs> event) { replicationInformer.removeFailoverStatusChanged(event); } @Override public AutoCloseable forceReadFromMaster() { final int old = readStripingBase; readStripingBase = -1; return new AutoCloseable() { @Override public void close() throws Exception { readStripingBase = old; } }; } @Override public JsonDocumentMetadata head(final String key) { ensureIsNotNullOrEmpty(key, "key"); return executeWithReplication(HttpMethods.HEAD, new Function1<OperationMetadata, JsonDocumentMetadata>() { @Override public JsonDocumentMetadata apply(OperationMetadata operationMetadata) { return directHead(operationMetadata, key); } }); } @Override public RavenJObjectIterator streamQuery(final String index, final IndexQuery query, final Reference<QueryHeaderInformation> queryHeaderInfo) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, RavenJObjectIterator>() { @Override public RavenJObjectIterator apply(OperationMetadata operationMetadata) { return directStreamQuery(operationMetadata, index, query, queryHeaderInfo); } }); } public RavenJObjectIterator directStreamQuery(OperationMetadata operationMetadata, String index, IndexQuery query, Reference<QueryHeaderInformation> queryHeaderInfo) { ensureIsNotNullOrEmpty(index, "index"); String path; HttpMethods method; if (query.getQuery() != null && query.getQuery().length() > convention.getMaxLengthOfQueryUsingGetUrl()) { path = query.getIndexQueryUrl(operationMetadata.getUrl(), index, "streams/query", false, false); method = HttpMethods.POST; } else { method = HttpMethods.GET; path = query.getIndexQueryUrl(operationMetadata.getUrl(), index, "streams/query", false); } HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, method, new RavenJObject(), credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(getUrl(), url, replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); request.removeAuthorizationHeader(); String token = getSingleAuthToken(operationMetadata); try { token = validateThatWeCanUseAuthenticateTokens(operationMetadata, token); } catch (Exception e) { request.close(); throw new IllegalStateException( "Could not authenticate token for query streaming, if you are using ravendb in IIS make sure you have Anonymous Authentication enabled in the IIS configuration", e); } request.addOperationHeader("Single-Use-Auth-Token", token); CloseableHttpResponse response; try { if (HttpMethods.POST.equals(method)) { response = request.executeRawResponse(query.getQuery()); } else { response = request.executeRawResponse(); } HttpJsonRequestExtension.assertNotFailingResponse(response); } catch (Exception e) { request.close(); if (index.startsWith("dynamic/") && request.getResponseStatusCode() == HttpStatus.SC_NOT_FOUND) { throw new IllegalStateException( "StreamQuery does not support querying dynamic indexes. It is designed to be used with large data-sets and is unlikely to return all data-set after 15 sec of indexing, like query() does", e); } throw new IllegalStateException(e.getMessage(), e); } QueryHeaderInformation queryHeaderInformation = new QueryHeaderInformation(); Map<String, String> headers = HttpJsonRequest.extractHeaders(response.getAllHeaders()); queryHeaderInformation.setIndex(headers.get("Raven-Index")); NetDateFormat sdf = new NetDateFormat(); try { queryHeaderInformation.setIndexTimestamp(sdf.parse(headers.get("Raven-Index-Timestamp"))); } catch (ParseException e) { throw new RuntimeException(e); } queryHeaderInformation.setIndexEtag(Etag.parse(headers.get("Raven-Index-Etag"))); queryHeaderInformation.setResultEtag(Etag.parse(headers.get("Raven-Result-Etag"))); queryHeaderInformation.setStale(Boolean.valueOf(headers.get("Raven-Is-Stale"))); queryHeaderInformation.setTotalResults(Integer.valueOf(headers.get("Raven-Total-Results"))); queryHeaderInfo.value = queryHeaderInformation; return yieldStreamResults(response); } private static RavenJObjectIterator yieldStreamResults(final CloseableHttpResponse webResponse) { HttpEntity httpEntity = webResponse.getEntity(); try { InputStream stream = httpEntity.getContent(); JsonParser jsonParser = new JsonFactory().createJsonParser(stream); if (jsonParser.nextToken() == null || jsonParser.getCurrentToken() != JsonToken.START_OBJECT) { throw new IllegalStateException("Unexpected data at start of stream"); } if (jsonParser.nextToken() == null || jsonParser.getCurrentToken() != JsonToken.FIELD_NAME || !"Results".equals(jsonParser.getText())) { throw new IllegalStateException("Unexpected data at stream 'Results' property name"); } if (jsonParser.nextToken() == null || jsonParser.getCurrentToken() != JsonToken.START_ARRAY) { throw new IllegalStateException("Unexpected data at 'Results', could not find start results array"); } return new RavenJObjectIterator(webResponse, jsonParser); } catch (IOException e) { throw new JsonReaderException(e); } } @Override public RavenJObjectIterator streamDocs() { return streamDocs(null, null, null, 0, Integer.MAX_VALUE); } @Override public RavenJObjectIterator streamDocs(Etag fromEtag) { return streamDocs(fromEtag, null, null, 0, Integer.MAX_VALUE, null); } @Override public RavenJObjectIterator streamDocs(Etag fromEtag, String startsWith) { return streamDocs(fromEtag, startsWith, null, 0, Integer.MAX_VALUE, null); } @Override public RavenJObjectIterator streamDocs(Etag fromEtag, String startsWith, String matches) { return streamDocs(fromEtag, startsWith, matches, 0, Integer.MAX_VALUE, null); } @Override public RavenJObjectIterator streamDocs(Etag fromEtag, String startsWith, String matches, int start) { return streamDocs(fromEtag, startsWith, matches, start, Integer.MAX_VALUE, null); } @Override public RavenJObjectIterator streamDocs(Etag fromEtag, String startsWith, String matches, int start, int pageSize) { return streamDocs(fromEtag, startsWith, matches, start, pageSize, null); } @Override public RavenJObjectIterator streamDocs(Etag fromEtag, String startsWith, String matches, int start, int pageSize, String exclude) { return streamDocs(fromEtag, startsWith, matches, start, pageSize, exclude, null); } @Override public RavenJObjectIterator streamDocs(Etag fromEtag, String startsWith, String matches, int start, int pageSize, String exclude, RavenPagingInformation pagingInformation) { return streamDocs(fromEtag, startsWith, matches, start, pageSize, exclude, pagingInformation, null); } @SuppressWarnings("null") @Override public RavenJObjectIterator streamDocs(final Etag fromEtag, final String startsWith, final String matches, final int start, final int pageSize, final String exclude, final RavenPagingInformation pagingInformation, final String skipAfter) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, RavenJObjectIterator>() { @Override public RavenJObjectIterator apply(OperationMetadata operationMetadata) { return directStreamDocs(operationMetadata, fromEtag, startsWith, matches, start, pageSize, exclude, pagingInformation, skipAfter); } }); } public RavenJObjectIterator directStreamDocs(OperationMetadata operationMetadata, final Etag fromEtag, final String startsWith, final String matches, final int start, final int pageSize, final String exclude, final RavenPagingInformation pagingInformation, final String skipAfter) { if (fromEtag != null && startsWith != null) throw new IllegalArgumentException( "Either fromEtag or startsWith must be null, you can't specify both"); StringBuilder sb = new StringBuilder(url).append("/streams/docs?"); if (fromEtag != null) { sb.append("etag=").append(fromEtag).append("&"); } else { if (startsWith != null) { sb.append("startsWith=").append(UrlUtils.escapeDataString(startsWith)).append("&"); } if (matches != null) { sb.append("matches=").append(UrlUtils.escapeDataString(matches)).append("&"); } if (exclude != null) { sb.append("exclude=").append(UrlUtils.escapeDataString(exclude)).append("&"); } if (skipAfter != null) { sb.append("skipAfter=").append(UrlUtils.escapeDataString(skipAfter)).append("&"); } } int actualStart = start; boolean nextPage = pagingInformation != null && pagingInformation.isForPreviousPage(start, pageSize); if (nextPage) { actualStart = pagingInformation.getNextPageStart(); } if (actualStart != 0) { sb.append("start=").append(actualStart).append("&"); } if (pageSize != Integer.MAX_VALUE) { sb.append("pageSize=").append(pageSize).append("&"); } if (nextPage) { sb.append("next-page=true").append("&"); } HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, sb.toString(), HttpMethods.GET, new RavenJObject(), credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, url, replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); request.removeAuthorizationHeader(); String token = getSingleAuthToken(operationMetadata); try { token = validateThatWeCanUseAuthenticateTokens(operationMetadata, token); } catch (Exception e) { request.close(); throw new IllegalStateException( "Could not authenticate token for docs streaming, if you are using ravendb in IIS make sure you have Anonymous Authentication enabled in the IIS configuration", e); } request.addOperationHeader("Single-Use-Auth-Token", token); CloseableHttpResponse response = null; try { response = request.executeRawResponse(); HttpJsonRequestExtension.assertNotFailingResponse(response); } catch (Exception e) { request.close(); throw new IllegalStateException(e.getMessage(), e); } return yieldStreamResults(response); } @Override public void delete(final String key, final Etag etag) { ensureIsNotNullOrEmpty(key, "key"); executeWithReplication(HttpMethods.DELETE, new Function1<OperationMetadata, Void>() { @Override public Void apply(OperationMetadata operationMetadata) { directDelete(operationMetadata, key, etag); return null; } }); } protected void directDelete(OperationMetadata operationMetadata, String key, Etag etag) { ensureIsNotNullOrEmpty(key, "key"); try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/docs/" + UrlUtils.escapeDataString(key), HttpMethods.DELETE, new RavenJObject(), operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { jsonRequest.executeRequest(); } } @Override public String urlFor(String documentKey) { return url + "/docs/" + documentKey; } @Override public ILowLevelBulkInsertOperation getBulkInsertOperation(BulkInsertOptions options, IDatabaseChanges changes) { return new RemoteBulkInsertOperation(options, this, changes); } protected JsonDocumentMetadata directHead(OperationMetadata operationMetadata, String key) { try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(ServerClient.this, operationMetadata.getUrl() + "/docs/" + key, HttpMethods.HEAD, new RavenJObject(), operationMetadata.getCredentials(), convention).addOperationHeaders(operationsHeaders)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { try { jsonRequest.executeRequest(); return SerializationHelper.deserializeJsonDocumentMetadata(key, jsonRequest.getResponseHeaders(), jsonRequest.getResponseStatusCode()); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) return null; if (e.getStatusCode() == HttpStatus.SC_CONFLICT) { ConflictException conflictException = new ConflictException("Conflict detected on " + key + ", conflict must be resolved before the document will be accessible. Cannot get the conflicts ids because" + " a HEAD request was performed. A GET request will provide more information, and if you have a document conflict listener, will automatically resolve the conflict", true); conflictException.setEtag(e.getEtag()); throw conflictException; } throw e; } } } public RavenJToken executeGetRequest(final String requestUrl) { ensureIsNotNullOrEmpty(requestUrl, "url"); return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, RavenJToken>() { @Override public RavenJToken apply(OperationMetadata operationMetadata) { RavenJObject metadata = new RavenJObject(); try (HttpJsonRequest jsonRequest = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(ServerClient.this, operationMetadata.getUrl() + requestUrl, HttpMethods.GET, metadata, operationMetadata.getCredentials(), convention) .addOperationHeaders(operationsHeaders))) { return jsonRequest.readResponseJson(); } } }); } public HttpJsonRequest createRequest(HttpMethods method, String requestUrl) { return createRequest(method, requestUrl, false, false, null); } public HttpJsonRequest createRequest(HttpMethods method, String requestUrl, boolean disableRequestCompression, boolean disableAuthentication, Long timeout) { RavenJObject metadata = new RavenJObject(); CreateHttpJsonRequestParams createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, url + requestUrl, method, metadata, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, timeout).addOperationHeaders(operationsHeaders); createHttpJsonRequestParams.setDisableRequestCompression(disableRequestCompression); createHttpJsonRequestParams.setDisableAuthentication(disableAuthentication); return jsonRequestFactory.createHttpJsonRequest(createHttpJsonRequestParams); } public HttpJsonRequest createRequest(OperationMetadata operationMetadata, HttpMethods method, String requestUrl, boolean disableRequestCompression, boolean disableAuthentication, Long timeout) { RavenJObject metadata = new RavenJObject(); CreateHttpJsonRequestParams createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + requestUrl, method, metadata, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, timeout) .addOperationHeaders(operationsHeaders); createHttpJsonRequestParams.setDisableRequestCompression(disableRequestCompression); createHttpJsonRequestParams.setDisableAuthentication(disableAuthentication); return jsonRequestFactory.createHttpJsonRequest(createHttpJsonRequestParams); } public HttpJsonRequest createReplicationAwareRequest(String currentServerUrl, String requestUrl, HttpMethods method) { return createReplicationAwareRequest(currentServerUrl, requestUrl, method, false); } public HttpJsonRequest createReplicationAwareRequest(String currentServerUrl, String requestUrl, HttpMethods method, boolean disableRequestCompression) { RavenJObject metadata = new RavenJObject(); CreateHttpJsonRequestParams createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, url + requestUrl, method, metadata, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention).addOperationHeaders(operationsHeaders); createHttpJsonRequestParams.setDisableRequestCompression(disableRequestCompression); return jsonRequestFactory.createHttpJsonRequest(createHttpJsonRequestParams).addReplicationStatusHeaders( url, currentServerUrl, replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback()); } @Override @Deprecated public void updateAttachmentMetadata(final String key, final Etag etag, final RavenJObject metadata) { executeWithReplication(HttpMethods.POST, new Function1<OperationMetadata, Void>() { @Override public Void apply(OperationMetadata operationMetadata) { directUpdateAttachmentMetadata(key, metadata, etag, operationMetadata); return null; } }); } @Deprecated protected void directUpdateAttachmentMetadata(String key, RavenJObject metadata, Etag etag, OperationMetadata operationMetadata) { if (etag != null) { metadata.set(Constants.METADATA_ETAG_FIELD, new RavenJValue(etag.toString())); } try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest( new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/static/" + key, HttpMethods.POST, metadata, operationMetadata.getCredentials(), convention)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { ErrorResponseException responseException; try { jsonRequest.executeRequest(); return; } catch (ErrorResponseException e) { responseException = e; } if (!handleException(responseException)) throw responseException; } } @Override @Deprecated public List<Attachment> getAttachmentHeadersStartingWith(final String idPrefix, final int start, final int pageSize) { return executeWithReplication(HttpMethods.GET, new Function1<OperationMetadata, List<Attachment>>() { @Override public List<Attachment> apply(OperationMetadata operationMetadata) { return directGetAttachmentHeadersStartingWith(HttpMethods.GET, idPrefix, start, pageSize, operationMetadata); } }); } @Deprecated protected List<Attachment> directGetAttachmentHeadersStartingWith(HttpMethods method, String idPrefix, int start, int pageSize, OperationMetadata operationMetadata) { try (HttpJsonRequest jsonRequest = jsonRequestFactory .createHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/static/?startsWith=" + idPrefix + "&start=" + start + "&pageSize=" + pageSize, method, new RavenJObject(), operationMetadata.getCredentials(), convention)) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { RavenJToken responseJson = jsonRequest.readResponseJson(); // this method not-exists in .net version return SerializationHelper.deserializeAttachements(responseJson, false); } } protected void handleReplicationStatusChanges(Map<String, String> headers, String primaryUrl, String currentUrl) { if (!primaryUrl.equalsIgnoreCase(currentUrl)) { String forceCheck = headers.get(Constants.RAVEN_FORCE_PRIMARY_SERVER_CHECK); boolean shouldForceCheck; if (StringUtils.isNotEmpty(forceCheck)) { shouldForceCheck = Boolean.valueOf(forceCheck); replicationInformer.forceCheck(primaryUrl, shouldForceCheck); } } } public <S> S executeWithReplication(HttpMethods method, Function1<OperationMetadata, S> operation) { int currentRequest = ++requestCount; return replicationInformer.executeWithReplication(method, url, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, currentRequest, readStripingBase, operation); } private boolean assertNonConflictedDocumentAndCheckIfNeedToReload(OperationMetadata operationMetadata, RavenJObject docResult, Function1<String, ConflictException> onClictedQueryResult) { if (docResult == null) { return false; } RavenJToken metadata = docResult.get(Constants.METADATA); if (metadata == null) { return false; } if (metadata.value(Integer.TYPE, "@Http-Status-Code") == 409) { ConflictException concurrencyException = tryResolveConflictOrCreateConcurrencyException( operationMetadata, metadata.value(String.class, "@id"), docResult, HttpExtensions.etagHeaderToEtag(metadata.value(String.class, "@etag"))); if (concurrencyException == null) { return true; } throw concurrencyException; } if (metadata.value(Boolean.TYPE, Constants.RAVEN_REPLICATION_CONFLICT) && onClictedQueryResult != null) { throw onClictedQueryResult.apply(metadata.value(String.class, "@id")); } return false; } private ConflictException tryResolveConflictOrCreateConcurrencyException(OperationMetadata operationMetadata, String key, RavenJObject conflictsDoc, Etag etag) { RavenJArray ravenJArray = conflictsDoc.value(RavenJArray.class, "Conflicts"); if (ravenJArray == null) { throw new IllegalArgumentException( "Could not get conflict ids from conflicted document, are you trying to resolve a conflict when using metadata-only?"); } List<String> conflictIds = new ArrayList<>(); for (RavenJToken token : ravenJArray) { conflictIds.add(token.value(String.class)); } boolean result = tryResolveConflictByUsingRegisteredListeners(operationMetadata, key, etag, conflictIds, operationMetadata); if (result) return null; ConflictException conflictException = new ConflictException("Conflict detected on " + key + ", conflict must be resolved before the document will be accessible", true); conflictException.setConflictedVersionIds(conflictIds.toArray(new String[0])); conflictException.setEtag(etag); return conflictException; } @Override public Boolean tryResolveConflictByUsingRegisteredListeners(OperationMetadata operationMetadata, String key, Etag etag, List<String> conflictedIds, OperationMetadata opUrl) { if (operationMetadata == null) { operationMetadata = new OperationMetadata(url); } if (conflictListeners.length > 0 && resolvingConflict == false) { resolvingConflict = true; try { MultiLoadResult multiLoadResult = get(conflictedIds.toArray(new String[0]), null); List<JsonDocument> results = new ArrayList<>(); for (RavenJObject r : multiLoadResult.getResults()) { results.add(SerializationHelper.toJsonDocument(r)); } for (IDocumentConflictListener conflictListener : conflictListeners) { Reference<JsonDocument> resolvedDocument = new Reference<>(); if (conflictListener.tryResolveConflict(key, results, resolvedDocument)) { put(key, etag, resolvedDocument.value.getDataAsJson(), resolvedDocument.value.getMetadata()); return true; } } } finally { resolvingConflict = false; } } return false; } private <T> T retryOperationBecauseOfConflict(OperationMetadata operationMetadata, List<RavenJObject> docResults, T currentResult, Function0<T> nextTry, Function1<String, ConflictException> onClictedQueryResult) { boolean requiresRetry = false; for (RavenJObject docResult : docResults) { requiresRetry |= assertNonConflictedDocumentAndCheckIfNeedToReload(operationMetadata, docResult, onClictedQueryResult); } if (!requiresRetry) { return currentResult; } if (resolvingConflictRetries) { throw new IllegalStateException( "Encountered another conflict after already resolving a conflict. Conflict resultion cannot recurse."); } resolvingConflictRetries = true; try { return nextTry.apply(); } finally { resolvingConflictRetries = false; } } public RavenJToken getOperationStatus(long id) { try (HttpJsonRequest request = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, url + "/operation/status?id=" + id, HttpMethods.GET, new RavenJObject(), credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention).addOperationHeaders(operationsHeaders))) { return request.readResponseJson(); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) return null; throw e; } } public String getSingleAuthToken(OperationMetadata operationMetadata) { try (HttpJsonRequest tokenRequest = createRequest(operationMetadata, HttpMethods.GET, "/singleAuthToken", true, true, null)) { return tokenRequest.readResponseJson().value(String.class, "Token"); } } private String validateThatWeCanUseAuthenticateTokens(OperationMetadata operationMetadata, String token) { try (HttpJsonRequest request = createRequest(operationMetadata, HttpMethods.GET, "/singleAuthToken", true, true, null)) { request.removeAuthorizationHeader(); request.addOperationHeader("Single-Use-Auth-Token", token); RavenJToken result = request.readResponseJson(); return result.value(String.class, "Token"); } } public boolean isInFailoverMode() { return replicationInformer.getFailureCount(url).longValue() > 0; } public class HandleReplicationStatusChangesCallback implements Action3<Map<String, String>, String, String> { @Override public void apply(Map<String, String> headers, String primaryUrl, String currentUrl) { handleReplicationStatusChanges(headers, primaryUrl, currentUrl); } } @Override public DatabaseStatistics getStatistics() { try (HttpJsonRequest httpJsonRequest = jsonRequestFactory.createHttpJsonRequest( new CreateHttpJsonRequestParams(this, url + "/stats", HttpMethods.GET, new RavenJObject(), credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention))) { RavenJObject jo = (RavenJObject) httpJsonRequest.readResponseJson(); return convention.createSerializer().deserialize(jo, DatabaseStatistics.class); } } @Override public boolean isExpect100Continue() { return expect100Continue; } public void setExpect100Continue(boolean expect100Continue) { this.expect100Continue = expect100Continue; } public ReplicationDocument directGetReplicationDestinations(OperationMetadata operationMetadata) { CreateHttpJsonRequestParams createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, operationMetadata.getUrl() + "/replication/topology", HttpMethods.GET, null, operationMetadata.getCredentials(), convention); try (HttpJsonRequest request = jsonRequestFactory .createHttpJsonRequest(createHttpJsonRequestParams.addOperationHeaders(getOperationsHeaders())) .addReplicationStatusHeaders(url, operationMetadata.getUrl(), replicationInformer, convention.getFailoverBehavior(), new HandleReplicationStatusChangesCallback())) { try { RavenJToken requestJson = request.readResponseJson(); return convention.createSerializer().deserialize(requestJson, ReplicationDocument.class); } catch (ErrorResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND || e.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { return null; } throw e; } } } public HttpJsonRequestFactory getJsonRequestFactory() { return jsonRequestFactory; } }