mobile/android/base/background/bagheera/BagheeraClient.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 package org.mozilla.gecko.background.bagheera;
     7 import java.io.IOException;
     8 import java.net.URISyntaxException;
     9 import java.security.GeneralSecurityException;
    10 import java.util.Collection;
    11 import java.util.concurrent.Executor;
    12 import java.util.concurrent.Executors;
    13 import java.util.regex.Pattern;
    15 import org.mozilla.gecko.sync.Utils;
    16 import org.mozilla.gecko.sync.net.BaseResource;
    17 import org.mozilla.gecko.sync.net.BaseResourceDelegate;
    18 import org.mozilla.gecko.sync.net.Resource;
    20 import ch.boye.httpclientandroidlib.HttpEntity;
    21 import ch.boye.httpclientandroidlib.HttpResponse;
    22 import ch.boye.httpclientandroidlib.client.ClientProtocolException;
    23 import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
    24 import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
    25 import ch.boye.httpclientandroidlib.protocol.HTTP;
    27 /**
    28  * Provides encapsulated access to a Bagheera document server.
    29  * The two permitted operations are:
    30  * * Delete a document.
    31  * * Upload a document, optionally deleting an expired document.
    32  */
    33 public class BagheeraClient {
    35   protected final String serverURI;
    36   protected final Executor executor;
    37   protected static final Pattern URI_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$");
    39   protected static String PROTOCOL_VERSION = "1.0";
    40   protected static String SUBMIT_PATH = "/submit/";
    42   /**
    43    * Instantiate a new client pointing at the provided server.
    44    * {@link #deleteDocument(String, String, BagheeraRequestDelegate)} and
    45    * {@link #uploadJSONDocument(String, String, String, String, BagheeraRequestDelegate)}
    46    * both accept delegate arguments; the {@link Executor} provided to this
    47    * constructor will be used to invoke callbacks on those delegates.
    48    *
    49    * @param serverURI
    50    *          the destination server URI.
    51    * @param executor
    52    *          the executor which will be used to invoke delegate callbacks.
    53    */
    54   public BagheeraClient(final String serverURI, final Executor executor) {
    55     if (serverURI == null) {
    56       throw new IllegalArgumentException("Must provide a server URI.");
    57     }
    58     if (executor == null) {
    59       throw new IllegalArgumentException("Must provide a non-null executor.");
    60     }
    61     this.serverURI = serverURI.endsWith("/") ? serverURI : serverURI + "/";
    62     this.executor = executor;
    63   }
    65   /**
    66    * Instantiate a new client pointing at the provided server.
    67    * Delegate callbacks will be invoked on a new background thread.
    68    *
    69    * See {@link #BagheeraClient(String, Executor)} for more details.
    70    *
    71    * @param serverURI
    72    *          the destination server URI.
    73    */
    74   public BagheeraClient(final String serverURI) {
    75     this(serverURI, Executors.newSingleThreadExecutor());
    76   }
    78   /**
    79    * Delete the specified document from the server.
    80    * The delegate's callbacks will be invoked by the BagheeraClient's executor.
    81    */
    82   public void deleteDocument(final String namespace,
    83                              final String id,
    84                              final BagheeraRequestDelegate delegate) throws URISyntaxException {
    85     if (namespace == null) {
    86       throw new IllegalArgumentException("Must provide namespace.");
    87     }
    88     if (id == null) {
    89       throw new IllegalArgumentException("Must provide id.");
    90     }
    92     final BaseResource resource = makeResource(namespace, id);
    93     resource.delegate = new BagheeraResourceDelegate(resource, namespace, id, delegate);
    94     resource.delete();
    95   }
    97   /**
    98    * Upload a JSON document to a Bagheera server. The delegate's callbacks will
    99    * be invoked in tasks run by the client's executor.
   100    *
   101    * @param namespace
   102    *          the namespace, such as "test"
   103    * @param id
   104    *          the document ID, which is typically a UUID.
   105    * @param payload
   106    *          a document, typically JSON-encoded.
   107    * @param oldIDs
   108    *          an optional collection of IDs which denote documents to supersede. Can be null or empty.
   109    * @param delegate
   110    *          the delegate whose methods should be invoked on success or
   111    *          failure.
   112    */
   113   public void uploadJSONDocument(final String namespace,
   114                                  final String id,
   115                                  final String payload,
   116                                  Collection<String> oldIDs,
   117                                  final BagheeraRequestDelegate delegate) throws URISyntaxException {
   118     if (namespace == null) {
   119       throw new IllegalArgumentException("Must provide namespace.");
   120     }
   121     if (id == null) {
   122       throw new IllegalArgumentException("Must provide id.");
   123     }
   124     if (payload == null) {
   125       throw new IllegalArgumentException("Must provide payload.");
   126     }
   128     final BaseResource resource = makeResource(namespace, id);
   129     final HttpEntity deflatedBody = DeflateHelper.deflateBody(payload);
   131     resource.delegate = new BagheeraUploadResourceDelegate(resource, namespace, id, oldIDs, delegate);
   132     resource.post(deflatedBody);
   133   }
   135   public static boolean isValidURIComponent(final String in) {
   136     return URI_PATTERN.matcher(in).matches();
   137   }
   139   protected BaseResource makeResource(final String namespace, final String id) throws URISyntaxException {
   140     if (!isValidURIComponent(namespace)) {
   141       throw new URISyntaxException(namespace, "Illegal namespace name. Must be alphanumeric + [_-].");
   142     }
   144     if (!isValidURIComponent(id)) {
   145       throw new URISyntaxException(id, "Illegal id value. Must be alphanumeric + [_-].");
   146     }
   148     final String uri = this.serverURI + PROTOCOL_VERSION + SUBMIT_PATH +
   149                        namespace + "/" + id;
   150     return new BaseResource(uri);
   151   }
   153   public class BagheeraResourceDelegate extends BaseResourceDelegate {
   154     private static final int DEFAULT_SOCKET_TIMEOUT_MSEC = 5 * 60 * 1000;       // Five minutes.
   155     protected final BagheeraRequestDelegate delegate;
   156     protected final String namespace;
   157     protected final String id;
   159     public BagheeraResourceDelegate(final Resource resource,
   160                                     final String namespace,
   161                                     final String id,
   162                                     final BagheeraRequestDelegate delegate) {
   163       super(resource);
   164       this.namespace = namespace;
   165       this.id = id;
   166       this.delegate = delegate;
   167     }
   169     @Override
   170     public String getUserAgent() {
   171       return delegate.getUserAgent();
   172     }
   174     @Override
   175     public int socketTimeout() {
   176       return DEFAULT_SOCKET_TIMEOUT_MSEC;
   177     }
   179     @Override
   180     public void handleHttpResponse(HttpResponse response) {
   181       final int status = response.getStatusLine().getStatusCode();
   182       switch (status) {
   183       case 200:
   184       case 201:
   185         invokeHandleSuccess(status, response);
   186         return;
   187       default:
   188         invokeHandleFailure(status, response);
   189       }
   190     }
   192     protected void invokeHandleError(final Exception e) {
   193       executor.execute(new Runnable() {
   194         @Override
   195         public void run() {
   196           delegate.handleError(e);
   197         }
   198       });
   199     }
   201     protected void invokeHandleFailure(final int status, final HttpResponse response) {
   202       executor.execute(new Runnable() {
   203         @Override
   204         public void run() {
   205           delegate.handleFailure(status, namespace, response);
   206         }
   207       });
   208     }
   210     protected void invokeHandleSuccess(final int status, final HttpResponse response) {
   211       executor.execute(new Runnable() {
   212         @Override
   213         public void run() {
   214           delegate.handleSuccess(status, namespace, id, response);
   215         }
   216       });
   217     }
   219     @Override
   220     public void handleHttpProtocolException(final ClientProtocolException e) {
   221       invokeHandleError(e);
   222     }
   224     @Override
   225     public void handleHttpIOException(IOException e) {
   226       invokeHandleError(e);
   227     }
   229     @Override
   230     public void handleTransportException(GeneralSecurityException e) {
   231       invokeHandleError(e);
   232     }
   233   }
   235   public final class BagheeraUploadResourceDelegate extends BagheeraResourceDelegate {
   236     private static final String HEADER_OBSOLETE_DOCUMENT = "X-Obsolete-Document";
   237     private static final String COMPRESSED_CONTENT_TYPE = "application/json+zlib; charset=utf-8";
   238     protected final Collection<String> obsoleteDocumentIDs;
   240     public BagheeraUploadResourceDelegate(Resource resource,
   241         String namespace,
   242         String id,
   243         Collection<String> obsoleteDocumentIDs,
   244         BagheeraRequestDelegate delegate) {
   245       super(resource, namespace, id, delegate);
   246       this.obsoleteDocumentIDs = obsoleteDocumentIDs;
   247     }
   249     @Override
   250     public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
   251       super.addHeaders(request, client);
   252       request.setHeader(HTTP.CONTENT_TYPE, COMPRESSED_CONTENT_TYPE);
   253       if (this.obsoleteDocumentIDs != null && this.obsoleteDocumentIDs.size() > 0) {
   254         request.addHeader(HEADER_OBSOLETE_DOCUMENT, Utils.toCommaSeparatedString(this.obsoleteDocumentIDs));
   255       }
   256     }
   257   }
   258 }

mercurial