mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
248 lines
8.4 KiB
Java
248 lines
8.4 KiB
Java
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package org.mozilla.gecko.background.bagheera;
|
|
|
|
import java.io.IOException;
|
|
import java.net.URISyntaxException;
|
|
import java.security.GeneralSecurityException;
|
|
import java.util.concurrent.Executor;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.regex.Pattern;
|
|
|
|
import org.mozilla.gecko.sync.net.BaseResource;
|
|
import org.mozilla.gecko.sync.net.BaseResourceDelegate;
|
|
import org.mozilla.gecko.sync.net.Resource;
|
|
|
|
import ch.boye.httpclientandroidlib.HttpEntity;
|
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
|
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
|
import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
|
|
import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
|
|
import ch.boye.httpclientandroidlib.protocol.HTTP;
|
|
|
|
/**
|
|
* Provides encapsulated access to a Bagheera document server.
|
|
* The two permitted operations are:
|
|
* * Delete a document.
|
|
* * Upload a document, optionally deleting an expired document.
|
|
*/
|
|
public class BagheeraClient {
|
|
|
|
protected final String serverURI;
|
|
protected final Executor executor;
|
|
protected static final Pattern URI_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
|
|
|
|
protected static String PROTOCOL_VERSION = "1.0";
|
|
protected static String SUBMIT_PATH = "/submit/";
|
|
|
|
/**
|
|
* Instantiate a new client pointing at the provided server.
|
|
* {@link #deleteDocument(String, String, BagheeraRequestDelegate)} and
|
|
* {@link #uploadJSONDocument(String, String, String, String, BagheeraRequestDelegate)}
|
|
* both accept delegate arguments; the {@link Executor} provided to this
|
|
* constructor will be used to invoke callbacks on those delegates.
|
|
*
|
|
* @param serverURI
|
|
* the destination server URI.
|
|
* @param executor
|
|
* the executor which will be used to invoke delegate callbacks.
|
|
*/
|
|
public BagheeraClient(final String serverURI, final Executor executor) {
|
|
if (serverURI == null) {
|
|
throw new IllegalArgumentException("Must provide a server URI.");
|
|
}
|
|
if (executor == null) {
|
|
throw new IllegalArgumentException("Must provide a non-null executor.");
|
|
}
|
|
this.serverURI = serverURI.endsWith("/") ? serverURI : serverURI + "/";
|
|
this.executor = executor;
|
|
}
|
|
|
|
/**
|
|
* Instantiate a new client pointing at the provided server.
|
|
* Delegate callbacks will be invoked on a new background thread.
|
|
*
|
|
* See {@link #BagheeraClient(String, Executor)} for more details.
|
|
*
|
|
* @param serverURI
|
|
* the destination server URI.
|
|
*/
|
|
public BagheeraClient(final String serverURI) {
|
|
this(serverURI, Executors.newSingleThreadExecutor());
|
|
}
|
|
|
|
/**
|
|
* Delete the specified document from the server.
|
|
* The delegate's callbacks will be invoked by the BagheeraClient's executor.
|
|
*/
|
|
public void deleteDocument(final String namespace,
|
|
final String id,
|
|
final BagheeraRequestDelegate delegate) throws URISyntaxException {
|
|
if (namespace == null) {
|
|
throw new IllegalArgumentException("Must provide namespace.");
|
|
}
|
|
if (id == null) {
|
|
throw new IllegalArgumentException("Must provide id.");
|
|
}
|
|
|
|
final BaseResource resource = makeResource(namespace, id);
|
|
resource.delegate = new BagheeraResourceDelegate(resource, delegate);
|
|
resource.delete();
|
|
}
|
|
|
|
/**
|
|
* Upload a JSON document to a Bagheera server. The delegate's callbacks will
|
|
* be invoked in tasks run by the client's executor.
|
|
*
|
|
* @param namespace
|
|
* the namespace, such as "test"
|
|
* @param id
|
|
* the document ID, which is typically a UUID.
|
|
* @param payload
|
|
* a document, typically JSON-encoded.
|
|
* @param oldID
|
|
* an optional ID which denotes a document to supersede. Can be null.
|
|
* @param delegate
|
|
* the delegate whose methods should be invoked on success or
|
|
* failure.
|
|
*/
|
|
public void uploadJSONDocument(final String namespace,
|
|
final String id,
|
|
final String payload,
|
|
final String oldID,
|
|
final BagheeraRequestDelegate delegate) throws URISyntaxException {
|
|
if (namespace == null) {
|
|
throw new IllegalArgumentException("Must provide namespace.");
|
|
}
|
|
if (id == null) {
|
|
throw new IllegalArgumentException("Must provide id.");
|
|
}
|
|
if (payload == null) {
|
|
throw new IllegalArgumentException("Must provide payload.");
|
|
}
|
|
|
|
final BaseResource resource = makeResource(namespace, id);
|
|
final HttpEntity deflatedBody = DeflateHelper.deflateBody(payload);
|
|
|
|
resource.delegate = new BagheeraUploadResourceDelegate(resource, oldID, delegate);
|
|
resource.post(deflatedBody);
|
|
}
|
|
|
|
public static boolean isValidURIComponent(final String in) {
|
|
return URI_PATTERN.matcher(in).matches();
|
|
}
|
|
|
|
protected BaseResource makeResource(final String namespace, final String id) throws URISyntaxException {
|
|
if (!isValidURIComponent(namespace)) {
|
|
throw new URISyntaxException(namespace, "Illegal namespace name. Must be alphanumeric + [_-].");
|
|
}
|
|
|
|
if (!isValidURIComponent(id)) {
|
|
throw new URISyntaxException(id, "Illegal id value. Must be alphanumeric + [_-].");
|
|
}
|
|
|
|
final String uri = this.serverURI + PROTOCOL_VERSION + SUBMIT_PATH +
|
|
namespace + "/" + id;
|
|
return new BaseResource(uri);
|
|
}
|
|
|
|
public class BagheeraResourceDelegate extends BaseResourceDelegate {
|
|
private static final int DEFAULT_SOCKET_TIMEOUT_MSEC = 5 * 60 * 1000; // Five minutes.
|
|
protected BagheeraRequestDelegate delegate;
|
|
|
|
public BagheeraResourceDelegate(Resource resource) {
|
|
super(resource);
|
|
}
|
|
|
|
public BagheeraResourceDelegate(final Resource resource,
|
|
final BagheeraRequestDelegate delegate) {
|
|
this(resource);
|
|
this.delegate = delegate;
|
|
}
|
|
|
|
@Override
|
|
public int socketTimeout() {
|
|
return DEFAULT_SOCKET_TIMEOUT_MSEC;
|
|
}
|
|
|
|
@Override
|
|
public void handleHttpResponse(HttpResponse response) {
|
|
final int status = response.getStatusLine().getStatusCode();
|
|
switch (status) {
|
|
case 200:
|
|
case 201:
|
|
invokeHandleSuccess(status, response);
|
|
return;
|
|
default:
|
|
invokeHandleFailure(status, response);
|
|
}
|
|
}
|
|
|
|
protected void invokeHandleError(final Exception e) {
|
|
executor.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
delegate.handleError(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected void invokeHandleFailure(final int status, final HttpResponse response) {
|
|
executor.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
delegate.handleFailure(status, response);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected void invokeHandleSuccess(final int status, final HttpResponse response) {
|
|
executor.execute(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
delegate.handleSuccess(status, response);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void handleHttpProtocolException(final ClientProtocolException e) {
|
|
invokeHandleError(e);
|
|
}
|
|
|
|
@Override
|
|
public void handleHttpIOException(IOException e) {
|
|
invokeHandleError(e);
|
|
}
|
|
|
|
@Override
|
|
public void handleTransportException(GeneralSecurityException e) {
|
|
invokeHandleError(e);
|
|
}
|
|
}
|
|
|
|
public final class BagheeraUploadResourceDelegate extends BagheeraResourceDelegate {
|
|
private static final String HEADER_OBSOLETE_DOCUMENT = "X-Obsolete-Document";
|
|
private static final String COMPRESSED_CONTENT_TYPE = "application/json+zlib; charset=utf-8";
|
|
protected String obsoleteDocumentID;
|
|
|
|
public BagheeraUploadResourceDelegate(Resource resource,
|
|
String obsoleteDocumentID,
|
|
BagheeraRequestDelegate delegate) {
|
|
super(resource, delegate);
|
|
this.obsoleteDocumentID = obsoleteDocumentID;
|
|
}
|
|
|
|
@Override
|
|
public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
|
|
super.addHeaders(request, client);
|
|
request.setHeader(HTTP.CONTENT_TYPE, COMPRESSED_CONTENT_TYPE);
|
|
if (this.obsoleteDocumentID != null) {
|
|
request.addHeader(HEADER_OBSOLETE_DOCUMENT, this.obsoleteDocumentID);
|
|
}
|
|
}
|
|
}
|
|
}
|