Browse Source

Repository GCS plugin new client library (#30168)

This does away with the deprecated `com.google.api-client:google-api-client:1.23`
and replaces it with `com.google.cloud:google-cloud-storage:1.28.0`.
It also changes security permissions for the repository-gcs plugin.
Albert Zaharovits 7 years ago
parent
commit
801973fa9f
77 changed files with 3398 additions and 899 deletions
  1. 7 5
      docs/plugins/repository-gcs.asciidoc
  2. 191 22
      plugins/repository-gcs/build.gradle
  3. 1 0
      plugins/repository-gcs/licenses/api-common-1.5.0.jar.sha1
  4. 27 0
      plugins/repository-gcs/licenses/api-common-LICENSE.txt
  5. 0 0
      plugins/repository-gcs/licenses/api-common-NOTICE.txt
  6. 0 1
      plugins/repository-gcs/licenses/commons-codec-1.10.jar.sha1
  7. 0 1
      plugins/repository-gcs/licenses/commons-logging-1.1.3.jar.sha1
  8. 1 0
      plugins/repository-gcs/licenses/gax-1.25.0.jar.sha1
  9. 27 0
      plugins/repository-gcs/licenses/gax-LICENSE.txt
  10. 0 0
      plugins/repository-gcs/licenses/gax-NOTICE.txt
  11. 1 0
      plugins/repository-gcs/licenses/gax-httpjson-0.40.0.jar.sha1
  12. 27 0
      plugins/repository-gcs/licenses/gax-httpjson-LICENSE.txt
  13. 0 0
      plugins/repository-gcs/licenses/gax-httpjson-NOTICE.txt
  14. 201 0
      plugins/repository-gcs/licenses/google-api-client-LICENSE.txt
  15. 0 0
      plugins/repository-gcs/licenses/google-api-client-NOTICE.txt
  16. 201 0
      plugins/repository-gcs/licenses/google-api-services-storage-v1-rev115-LICENSE.txt
  17. 0 0
      plugins/repository-gcs/licenses/google-api-services-storage-v1-rev115-NOTICE.txt
  18. 28 0
      plugins/repository-gcs/licenses/google-auth-LICENSE.txt
  19. 0 0
      plugins/repository-gcs/licenses/google-auth-NOTICE.txt
  20. 1 0
      plugins/repository-gcs/licenses/google-auth-library-credentials-0.9.1.jar.sha1
  21. 1 0
      plugins/repository-gcs/licenses/google-auth-library-oauth2-http-0.9.1.jar.sha1
  22. 201 0
      plugins/repository-gcs/licenses/google-cloud-LICENSE.txt
  23. 0 0
      plugins/repository-gcs/licenses/google-cloud-NOTICE.txt
  24. 1 0
      plugins/repository-gcs/licenses/google-cloud-core-1.28.0.jar.sha1
  25. 1 0
      plugins/repository-gcs/licenses/google-cloud-core-http-1.28.0.jar.sha1
  26. 1 0
      plugins/repository-gcs/licenses/google-cloud-storage-1.28.0.jar.sha1
  27. 0 0
      plugins/repository-gcs/licenses/google-http-LICENSE.txt
  28. 0 0
      plugins/repository-gcs/licenses/google-http-NOTICE.txt
  29. 1 0
      plugins/repository-gcs/licenses/google-http-client-appengine-1.23.0.jar.sha1
  30. 1 0
      plugins/repository-gcs/licenses/google-http-client-jackson-1.23.0.jar.sha1
  31. 28 0
      plugins/repository-gcs/licenses/google-oauth-client-LICENSE.txt
  32. 0 0
      plugins/repository-gcs/licenses/google-oauth-client-NOTICE.txt
  33. 1 0
      plugins/repository-gcs/licenses/grpc-context-1.9.0.jar.sha1
  34. 0 0
      plugins/repository-gcs/licenses/grpc-context-LICENSE.txt
  35. 0 0
      plugins/repository-gcs/licenses/grpc-context-NOTICE.txt
  36. 1 0
      plugins/repository-gcs/licenses/guava-20.0.jar.sha1
  37. 202 0
      plugins/repository-gcs/licenses/guava-LICENSE.txt
  38. 0 0
      plugins/repository-gcs/licenses/guava-NOTICE.txt
  39. 0 1
      plugins/repository-gcs/licenses/httpclient-4.5.2.jar.sha1
  40. 0 1
      plugins/repository-gcs/licenses/httpcore-4.4.5.jar.sha1
  41. 1 0
      plugins/repository-gcs/licenses/jackson-core-asl-1.9.13.jar.sha1
  42. 202 0
      plugins/repository-gcs/licenses/jackson-core-asl-LICENSE.txt
  43. 0 0
      plugins/repository-gcs/licenses/jackson-core-asl-NOTICE.txt
  44. 202 0
      plugins/repository-gcs/licenses/old/commons-codec-LICENSE.txt
  45. 0 0
      plugins/repository-gcs/licenses/old/commons-codec-NOTICE.txt
  46. 0 0
      plugins/repository-gcs/licenses/old/commons-logging-LICENSE.txt
  47. 0 0
      plugins/repository-gcs/licenses/old/commons-logging-NOTICE.txt
  48. 201 0
      plugins/repository-gcs/licenses/old/google-LICENSE.txt
  49. 0 0
      plugins/repository-gcs/licenses/old/google-NOTICE.txt
  50. 0 0
      plugins/repository-gcs/licenses/old/httpclient-LICENSE.txt
  51. 0 0
      plugins/repository-gcs/licenses/old/httpclient-NOTICE.txt
  52. 0 0
      plugins/repository-gcs/licenses/old/httpcore-LICENSE.txt
  53. 0 0
      plugins/repository-gcs/licenses/old/httpcore-NOTICE.txt
  54. 202 0
      plugins/repository-gcs/licenses/opencensus-LICENSE.txt
  55. 0 0
      plugins/repository-gcs/licenses/opencensus-NOTICE.txt
  56. 1 0
      plugins/repository-gcs/licenses/opencensus-api-0.11.1.jar.sha1
  57. 1 0
      plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.11.1.jar.sha1
  58. 1 0
      plugins/repository-gcs/licenses/proto-google-common-protos-1.8.0.jar.sha1
  59. 202 0
      plugins/repository-gcs/licenses/proto-google-common-protos-LICENSE.txt
  60. 0 0
      plugins/repository-gcs/licenses/proto-google-common-protos-NOTICE.txt
  61. 1 0
      plugins/repository-gcs/licenses/threetenbp-1.3.6.jar.sha1
  62. 31 0
      plugins/repository-gcs/licenses/threetenbp-LICENSE.txt
  63. 0 0
      plugins/repository-gcs/licenses/threetenbp-NOTICE.txt
  64. 2 6
      plugins/repository-gcs/qa/google-cloud-storage/build.gradle
  65. 239 107
      plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageTestServer.java
  66. 157 237
      plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java
  67. 69 31
      plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageClientSettings.java
  68. 3 75
      plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java
  69. 2 3
      plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java
  70. 98 77
      plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java
  71. 4 3
      plugins/repository-gcs/src/main/plugin-metadata/plugin-security.policy
  72. 54 0
      plugins/repository-gcs/src/test/java/com/google/cloud/storage/StorageRpcOptionUtils.java
  73. 37 0
      plugins/repository-gcs/src/test/java/com/google/cloud/storage/StorageTestUtils.java
  74. 1 1
      plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java
  75. 82 50
      plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageClientSettingsTests.java
  76. 45 59
      plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageServiceTests.java
  77. 408 219
      plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/MockStorage.java

+ 7 - 5
docs/plugins/repository-gcs.asciidoc

@@ -84,11 +84,7 @@ A service account file looks like this:
   "private_key_id": "...",
   "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
   "client_email": "service-account-for-your-repository@your-project-id.iam.gserviceaccount.com",
-  "client_id": "...",
-  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
-  "token_uri": "https://accounts.google.com/o/oauth2/token",
-  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
-  "client_x509_cert_url": "..."
+  "client_id": "..."
 }
 ----
 // NOTCONSOLE
@@ -178,6 +174,12 @@ are marked as `Secure`.
     a custom name can be useful to authenticate your cluster when requests
     statistics are logged in the Google Cloud Platform. Default to `repository-gcs`
 
+`project_id`::
+
+    The Google Cloud project id. This will be automatically infered from the credentials file but
+    can be specified explicitly. For example, it can be used to switch between projects when the
+    same credentials are usable for both the production and the development projects.
+
 [[repository-gcs-repository]]
 ==== Repository Settings
 

+ 191 - 22
plugins/repository-gcs/build.gradle

@@ -22,38 +22,207 @@ esplugin {
   classname 'org.elasticsearch.repositories.gcs.GoogleCloudStoragePlugin'
 }
 
-versions << [
-        'google': '1.23.0',
-]
-
 dependencies {
-  compile "com.google.apis:google-api-services-storage:v1-rev115-${versions.google}"
-  compile "com.google.api-client:google-api-client:${versions.google}"
-  compile "com.google.oauth-client:google-oauth-client:${versions.google}"
-  compile "org.apache.httpcomponents:httpclient:${versions.httpclient}"
-  compile "org.apache.httpcomponents:httpcore:${versions.httpcore}"
-  compile "commons-logging:commons-logging:${versions.commonslogging}"
-  compile "commons-codec:commons-codec:${versions.commonscodec}"
-  compile "com.google.http-client:google-http-client:${versions.google}"
-  compile "com.google.http-client:google-http-client-jackson2:${versions.google}"
+  compile 'com.google.cloud:google-cloud-storage:1.28.0'
+  compile 'com.google.cloud:google-cloud-core:1.28.0'
+  compile 'com.google.cloud:google-cloud-core-http:1.28.0'
+  compile 'com.google.auth:google-auth-library-oauth2-http:0.9.1'
+  compile 'com.google.auth:google-auth-library-credentials:0.9.1'
+  compile 'com.google.oauth-client:google-oauth-client:1.23.0'
+  compile 'com.google.http-client:google-http-client:1.23.0'
+  compile 'com.google.http-client:google-http-client-jackson:1.23.0'
+  compile 'com.google.http-client:google-http-client-jackson2:1.23.0'
+  compile 'com.google.http-client:google-http-client-appengine:1.23.0'
+  compile 'com.google.api-client:google-api-client:1.23.0'
+  compile 'com.google.api:gax:1.25.0'
+  compile 'com.google.api:gax-httpjson:0.40.0'
+  compile 'com.google.api:api-common:1.5.0'
+  compile 'com.google.api.grpc:proto-google-common-protos:1.8.0'
+  compile 'com.google.guava:guava:20.0'
+  compile 'com.google.apis:google-api-services-storage:v1-rev115-1.23.0'
+  compile 'org.codehaus.jackson:jackson-core-asl:1.9.13'
+  compile 'io.grpc:grpc-context:1.9.0'
+  compile 'io.opencensus:opencensus-api:0.11.1'
+  compile 'io.opencensus:opencensus-contrib-http-util:0.11.1'
+  compile 'org.threeten:threetenbp:1.3.6'
 }
 
 dependencyLicenses {
-  mapping from: /google-.*/, to: 'google'
+  mapping from: /google-cloud-.*/, to: 'google-cloud'
+  mapping from: /google-auth-.*/, to: 'google-auth'
+  mapping from: /google-http-.*/, to: 'google-http'
+  mapping from: /opencensus.*/, to: 'opencensus'
 }
 
 thirdPartyAudit.excludes = [
+        // uses internal java api: sun.misc.Unsafe
+        'com.google.common.cache.Striped64',
+        'com.google.common.cache.Striped64$1',
+        'com.google.common.cache.Striped64$Cell',
+        'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1',
+        'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$2',
+        'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$3',
+        'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper',
+        'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper$1',
+        'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray',
+        'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator',
+        'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1',
         // classes are missing
-        'com.google.common.base.Splitter',
-        'com.google.common.collect.Lists',
-        'javax.servlet.ServletContextEvent',
-        'javax.servlet.ServletContextListener',
-        'org.apache.avalon.framework.logger.Logger',
-        'org.apache.log.Hierarchy',
-        'org.apache.log.Logger',
+        'com.google.appengine.api.datastore.Blob',
+        'com.google.appengine.api.datastore.DatastoreService',
+        'com.google.appengine.api.datastore.DatastoreServiceFactory',
+        'com.google.appengine.api.datastore.Entity',
+        'com.google.appengine.api.datastore.Key',
+        'com.google.appengine.api.datastore.KeyFactory',
+        'com.google.appengine.api.datastore.PreparedQuery',
+        'com.google.appengine.api.datastore.Query',
+        'com.google.appengine.api.memcache.Expiration',
+        'com.google.appengine.api.memcache.MemcacheService',
+        'com.google.appengine.api.memcache.MemcacheServiceFactory',
+        'com.google.appengine.api.urlfetch.FetchOptions$Builder',
+        'com.google.appengine.api.urlfetch.FetchOptions',
+        'com.google.appengine.api.urlfetch.HTTPHeader',
+        'com.google.appengine.api.urlfetch.HTTPMethod',
+        'com.google.appengine.api.urlfetch.HTTPRequest',
+        'com.google.appengine.api.urlfetch.HTTPResponse',
+        'com.google.appengine.api.urlfetch.URLFetchService',
+        'com.google.appengine.api.urlfetch.URLFetchServiceFactory',
+        'com.google.gson.Gson',
+        'com.google.gson.GsonBuilder',
+        'com.google.gson.TypeAdapter',
+        'com.google.gson.stream.JsonReader',
+        'com.google.gson.stream.JsonWriter',
+        'com.google.iam.v1.Binding$Builder',
+        'com.google.iam.v1.Binding',
+        'com.google.iam.v1.Policy$Builder',
+        'com.google.iam.v1.Policy',
+        'com.google.protobuf.AbstractMessageLite$Builder',
+        'com.google.protobuf.AbstractParser',
+        'com.google.protobuf.Any$Builder',
+        'com.google.protobuf.Any',
+        'com.google.protobuf.AnyOrBuilder',
+        'com.google.protobuf.AnyProto',
+        'com.google.protobuf.Api$Builder',
+        'com.google.protobuf.Api',
+        'com.google.protobuf.ApiOrBuilder',
+        'com.google.protobuf.ApiProto',
+        'com.google.protobuf.ByteString',
+        'com.google.protobuf.CodedInputStream',
+        'com.google.protobuf.CodedOutputStream',
+        'com.google.protobuf.DescriptorProtos',
+        'com.google.protobuf.Descriptors$Descriptor',
+        'com.google.protobuf.Descriptors$EnumDescriptor',
+        'com.google.protobuf.Descriptors$EnumValueDescriptor',
+        'com.google.protobuf.Descriptors$FieldDescriptor',
+        'com.google.protobuf.Descriptors$FileDescriptor$InternalDescriptorAssigner',
+        'com.google.protobuf.Descriptors$FileDescriptor',
+        'com.google.protobuf.Descriptors$OneofDescriptor',
+        'com.google.protobuf.Duration$Builder',
+        'com.google.protobuf.Duration',
+        'com.google.protobuf.DurationOrBuilder',
+        'com.google.protobuf.DurationProto',
+        'com.google.protobuf.EmptyProto',
+        'com.google.protobuf.Enum$Builder',
+        'com.google.protobuf.Enum',
+        'com.google.protobuf.EnumOrBuilder',
+        'com.google.protobuf.ExtensionRegistry',
+        'com.google.protobuf.ExtensionRegistryLite',
+        'com.google.protobuf.FloatValue$Builder',
+        'com.google.protobuf.FloatValue',
+        'com.google.protobuf.FloatValueOrBuilder',
+        'com.google.protobuf.GeneratedMessage$GeneratedExtension',
+        'com.google.protobuf.GeneratedMessage',
+        'com.google.protobuf.GeneratedMessageV3$Builder',
+        'com.google.protobuf.GeneratedMessageV3$BuilderParent',
+        'com.google.protobuf.GeneratedMessageV3$FieldAccessorTable',
+        'com.google.protobuf.GeneratedMessageV3',
+        'com.google.protobuf.Internal$EnumLite',
+        'com.google.protobuf.Internal$EnumLiteMap',
+        'com.google.protobuf.Internal',
+        'com.google.protobuf.InvalidProtocolBufferException',
+        'com.google.protobuf.LazyStringArrayList',
+        'com.google.protobuf.LazyStringList',
+        'com.google.protobuf.MapEntry$Builder',
+        'com.google.protobuf.MapEntry',
+        'com.google.protobuf.MapField',
+        'com.google.protobuf.Message',
+        'com.google.protobuf.MessageOrBuilder',
+        'com.google.protobuf.Parser',
+        'com.google.protobuf.ProtocolMessageEnum',
+        'com.google.protobuf.ProtocolStringList',
+        'com.google.protobuf.RepeatedFieldBuilderV3',
+        'com.google.protobuf.SingleFieldBuilderV3',
+        'com.google.protobuf.Struct$Builder',
+        'com.google.protobuf.Struct',
+        'com.google.protobuf.StructOrBuilder',
+        'com.google.protobuf.StructProto',
+        'com.google.protobuf.Timestamp$Builder',
+        'com.google.protobuf.Timestamp',
+        'com.google.protobuf.TimestampProto',
+        'com.google.protobuf.Type$Builder',
+        'com.google.protobuf.Type',
+        'com.google.protobuf.TypeOrBuilder',
+        'com.google.protobuf.TypeProto',
+        'com.google.protobuf.UInt32Value$Builder',
+        'com.google.protobuf.UInt32Value',
+        'com.google.protobuf.UInt32ValueOrBuilder',
+        'com.google.protobuf.UnknownFieldSet$Builder',
+        'com.google.protobuf.UnknownFieldSet',
+        'com.google.protobuf.WireFormat$FieldType',
+        'com.google.protobuf.WrappersProto',
+        'com.google.protobuf.util.Timestamps',
+        'org.apache.http.ConnectionReuseStrategy',
+        'org.apache.http.Header',
+        'org.apache.http.HttpEntity',
+        'org.apache.http.HttpEntityEnclosingRequest',
+        'org.apache.http.HttpHost',
+        'org.apache.http.HttpRequest',
+        'org.apache.http.HttpResponse',
+        'org.apache.http.HttpVersion',
+        'org.apache.http.RequestLine',
+        'org.apache.http.StatusLine',
+        'org.apache.http.client.AuthenticationHandler',
+        'org.apache.http.client.HttpClient',
+        'org.apache.http.client.HttpRequestRetryHandler',
+        'org.apache.http.client.RedirectHandler',
+        'org.apache.http.client.RequestDirector',
+        'org.apache.http.client.UserTokenHandler',
+        'org.apache.http.client.methods.HttpDelete',
+        'org.apache.http.client.methods.HttpEntityEnclosingRequestBase',
+        'org.apache.http.client.methods.HttpGet',
+        'org.apache.http.client.methods.HttpHead',
+        'org.apache.http.client.methods.HttpOptions',
+        'org.apache.http.client.methods.HttpPost',
+        'org.apache.http.client.methods.HttpPut',
+        'org.apache.http.client.methods.HttpRequestBase',
+        'org.apache.http.client.methods.HttpTrace',
+        'org.apache.http.conn.ClientConnectionManager',
+        'org.apache.http.conn.ConnectionKeepAliveStrategy',
+        'org.apache.http.conn.params.ConnManagerParams',
+        'org.apache.http.conn.params.ConnPerRouteBean',
+        'org.apache.http.conn.params.ConnRouteParams',
+        'org.apache.http.conn.routing.HttpRoutePlanner',
+        'org.apache.http.conn.scheme.PlainSocketFactory',
+        'org.apache.http.conn.scheme.Scheme',
+        'org.apache.http.conn.scheme.SchemeRegistry',
+        'org.apache.http.conn.ssl.SSLSocketFactory',
+        'org.apache.http.conn.ssl.X509HostnameVerifier',
+        'org.apache.http.entity.AbstractHttpEntity',
+        'org.apache.http.impl.client.DefaultHttpClient',
+        'org.apache.http.impl.client.DefaultHttpRequestRetryHandler',
+        'org.apache.http.impl.conn.ProxySelectorRoutePlanner',
+        'org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager',
+        'org.apache.http.message.BasicHttpResponse',
+        'org.apache.http.params.BasicHttpParams',
+        'org.apache.http.params.HttpConnectionParams',
+        'org.apache.http.params.HttpParams',
+        'org.apache.http.params.HttpProtocolParams',
+        'org.apache.http.protocol.HttpContext',
+        'org.apache.http.protocol.HttpProcessor',
+        'org.apache.http.protocol.HttpRequestExecutor'
 ]
 
 check {
   // also execute the QA tests when testing the plugin
   dependsOn 'qa:google-cloud-storage:check'
-}
+}

+ 1 - 0
plugins/repository-gcs/licenses/api-common-1.5.0.jar.sha1

@@ -0,0 +1 @@
+7e537338d40a57ad469239acb6d828fa544fb52b

+ 27 - 0
plugins/repository-gcs/licenses/api-common-LICENSE.txt

@@ -0,0 +1,27 @@
+Copyright 2016, Google Inc.
+All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0 - 0
plugins/repository-gcs/licenses/api-common-NOTICE.txt


+ 0 - 1
plugins/repository-gcs/licenses/commons-codec-1.10.jar.sha1

@@ -1 +0,0 @@
-4b95f4897fa13f2cd904aee711aeafc0c5295cd8

+ 0 - 1
plugins/repository-gcs/licenses/commons-logging-1.1.3.jar.sha1

@@ -1 +0,0 @@
-f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f

+ 1 - 0
plugins/repository-gcs/licenses/gax-1.25.0.jar.sha1

@@ -0,0 +1 @@
+36ab73c0b5d4a67447eb89a3174cc76ced150bd1

+ 27 - 0
plugins/repository-gcs/licenses/gax-LICENSE.txt

@@ -0,0 +1,27 @@
+Copyright 2016, Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+    * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0 - 0
plugins/repository-gcs/licenses/gax-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/gax-httpjson-0.40.0.jar.sha1

@@ -0,0 +1 @@
+cb4bafbfd45b9d24efbb6138a31e37918fac015f

+ 27 - 0
plugins/repository-gcs/licenses/gax-httpjson-LICENSE.txt

@@ -0,0 +1,27 @@
+Copyright 2016, Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+    * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0 - 0
plugins/repository-gcs/licenses/gax-httpjson-NOTICE.txt


+ 201 - 0
plugins/repository-gcs/licenses/google-api-client-LICENSE.txt

@@ -0,0 +1,201 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/google-api-client-NOTICE.txt


+ 201 - 0
plugins/repository-gcs/licenses/google-api-services-storage-v1-rev115-LICENSE.txt

@@ -0,0 +1,201 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/google-api-services-storage-v1-rev115-NOTICE.txt


+ 28 - 0
plugins/repository-gcs/licenses/google-auth-LICENSE.txt

@@ -0,0 +1,28 @@
+Copyright 2014, Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0 - 0
plugins/repository-gcs/licenses/google-auth-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/google-auth-library-credentials-0.9.1.jar.sha1

@@ -0,0 +1 @@
+25e0f45f3b3d1b4fccc8944845e51a7a4f359652

+ 1 - 0
plugins/repository-gcs/licenses/google-auth-library-oauth2-http-0.9.1.jar.sha1

@@ -0,0 +1 @@
+c0fe3a39b0f28d59de1986b3c50f018cd7cb9ec2

+ 201 - 0
plugins/repository-gcs/licenses/google-cloud-LICENSE.txt

@@ -0,0 +1,201 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/google-cloud-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/google-cloud-core-1.28.0.jar.sha1

@@ -0,0 +1 @@
+c0e88c78ce17c92d76bf46345faf3fa68833b216

+ 1 - 0
plugins/repository-gcs/licenses/google-cloud-core-http-1.28.0.jar.sha1

@@ -0,0 +1 @@
+7b4559a9513abd98da50958c56a10f8ae00cb0f7

+ 1 - 0
plugins/repository-gcs/licenses/google-cloud-storage-1.28.0.jar.sha1

@@ -0,0 +1 @@
+226019ae816b42c59f1b06999aeeb73722b87200

+ 0 - 0
plugins/repository-gcs/licenses/google-LICENSE.txt → plugins/repository-gcs/licenses/google-http-LICENSE.txt


+ 0 - 0
plugins/repository-gcs/licenses/google-http-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/google-http-client-appengine-1.23.0.jar.sha1

@@ -0,0 +1 @@
+0eda0d0f758c1cc525866e52e1226c4eb579d130

+ 1 - 0
plugins/repository-gcs/licenses/google-http-client-jackson-1.23.0.jar.sha1

@@ -0,0 +1 @@
+a72ea3a197937ef63a893e73df312dac0d813663

+ 28 - 0
plugins/repository-gcs/licenses/google-oauth-client-LICENSE.txt

@@ -0,0 +1,28 @@
+Copyright 2014, Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0 - 0
plugins/repository-gcs/licenses/google-oauth-client-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/grpc-context-1.9.0.jar.sha1

@@ -0,0 +1 @@
+28b0836f48c9705abf73829bbc536dba29a1329a

+ 0 - 0
plugins/repository-gcs/licenses/commons-codec-LICENSE.txt → plugins/repository-gcs/licenses/grpc-context-LICENSE.txt


+ 0 - 0
plugins/repository-gcs/licenses/grpc-context-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/guava-20.0.jar.sha1

@@ -0,0 +1 @@
+89507701249388e1ed5ddcf8c41f4ce1be7831ef

+ 202 - 0
plugins/repository-gcs/licenses/guava-LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/guava-NOTICE.txt


+ 0 - 1
plugins/repository-gcs/licenses/httpclient-4.5.2.jar.sha1

@@ -1 +0,0 @@
-733db77aa8d9b2d68015189df76ab06304406e50

+ 0 - 1
plugins/repository-gcs/licenses/httpcore-4.4.5.jar.sha1

@@ -1 +0,0 @@
-e7501a1b34325abb00d17dde96150604a0658b54

+ 1 - 0
plugins/repository-gcs/licenses/jackson-core-asl-1.9.13.jar.sha1

@@ -0,0 +1 @@
+3c304d70f42f832e0a86d45bd437f692129299a4

+ 202 - 0
plugins/repository-gcs/licenses/jackson-core-asl-LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/jackson-core-asl-NOTICE.txt


+ 202 - 0
plugins/repository-gcs/licenses/old/commons-codec-LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/commons-codec-NOTICE.txt → plugins/repository-gcs/licenses/old/commons-codec-NOTICE.txt


+ 0 - 0
plugins/repository-gcs/licenses/commons-logging-LICENSE.txt → plugins/repository-gcs/licenses/old/commons-logging-LICENSE.txt


+ 0 - 0
plugins/repository-gcs/licenses/commons-logging-NOTICE.txt → plugins/repository-gcs/licenses/old/commons-logging-NOTICE.txt


+ 201 - 0
plugins/repository-gcs/licenses/old/google-LICENSE.txt

@@ -0,0 +1,201 @@
+                                Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/google-NOTICE.txt → plugins/repository-gcs/licenses/old/google-NOTICE.txt


+ 0 - 0
plugins/repository-gcs/licenses/httpclient-LICENSE.txt → plugins/repository-gcs/licenses/old/httpclient-LICENSE.txt


+ 0 - 0
plugins/repository-gcs/licenses/httpclient-NOTICE.txt → plugins/repository-gcs/licenses/old/httpclient-NOTICE.txt


+ 0 - 0
plugins/repository-gcs/licenses/httpcore-LICENSE.txt → plugins/repository-gcs/licenses/old/httpcore-LICENSE.txt


+ 0 - 0
plugins/repository-gcs/licenses/httpcore-NOTICE.txt → plugins/repository-gcs/licenses/old/httpcore-NOTICE.txt


+ 202 - 0
plugins/repository-gcs/licenses/opencensus-LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/opencensus-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/opencensus-api-0.11.1.jar.sha1

@@ -0,0 +1 @@
+54689fbf750a7f26e34fa1f1f96b883c53f51486

+ 1 - 0
plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.11.1.jar.sha1

@@ -0,0 +1 @@
+82e572b41e81ecf58d0d1e9a3953a05aa8f9c84b

+ 1 - 0
plugins/repository-gcs/licenses/proto-google-common-protos-1.8.0.jar.sha1

@@ -0,0 +1 @@
+b3282312ba82536fc9a7778cabfde149a875e877

+ 202 - 0
plugins/repository-gcs/licenses/proto-google-common-protos-LICENSE.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 0 - 0
plugins/repository-gcs/licenses/proto-google-common-protos-NOTICE.txt


+ 1 - 0
plugins/repository-gcs/licenses/threetenbp-1.3.6.jar.sha1

@@ -0,0 +1 @@
+89dcc04a7e028c3c963413a71f950703cf51f057

+ 31 - 0
plugins/repository-gcs/licenses/threetenbp-LICENSE.txt

@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of JSR-310 nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */

+ 0 - 0
plugins/repository-gcs/licenses/threetenbp-NOTICE.txt


+ 2 - 6
plugins/repository-gcs/qa/google-cloud-storage/build.gradle

@@ -69,7 +69,6 @@ task googleCloudStorageFixture(type: AntFixture) {
 
 /** A service account file that points to the Google Cloud Storage service emulated by the fixture **/
 task createServiceAccountFile() {
-    dependsOn googleCloudStorageFixture
     doLast {
         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA")
         keyPairGenerator.initialize(1024)
@@ -83,11 +82,7 @@ task createServiceAccountFile() {
                 '  "private_key_id": "' + UUID.randomUUID().toString() + '",\n' +
                 '  "private_key": "-----BEGIN PRIVATE KEY-----\\n' + encodedKey + '\\n-----END PRIVATE KEY-----\\n",\n' +
                 '  "client_email": "integration_test@appspot.gserviceaccount.com",\n' +
-                '  "client_id": "123456789101112130594",\n' +
-                "  \"auth_uri\": \"http://${googleCloudStorageFixture.addressAndPort}/o/oauth2/auth\",\n" +
-                "  \"token_uri\": \"http://${googleCloudStorageFixture.addressAndPort}/o/oauth2/token\",\n" +
-                '  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",\n' +
-                '  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/integration_test%40appspot.gserviceaccount.com"\n' +
+                '  "client_id": "123456789101112130594"\n' +
                 '}', 'UTF-8')
     }
 }
@@ -109,6 +104,7 @@ integTestCluster {
         dependsOn createServiceAccountFile, googleCloudStorageFixture
         /* Use a closure on the string to delay evaluation until tests are executed */
         setting 'gcs.client.integration_test.endpoint', "http://${ -> googleCloudStorageFixture.addressAndPort }"
+        setting 'gcs.client.integration_test.token_uri', "http://${ -> googleCloudStorageFixture.addressAndPort }/o/oauth2/token"
     } else {
         println "Using an external service to test the repository-gcs plugin"
     }

+ 239 - 107
plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageTestServer.java

@@ -31,13 +31,18 @@ import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
 
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonList;
@@ -52,7 +57,7 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
  */
 public class GoogleCloudStorageTestServer {
 
-    private static byte[] EMPTY_BYTE = new byte[0];
+    private static final byte[] EMPTY_BYTE = new byte[0];
 
     /** List of the buckets stored on this test server **/
     private final Map<String, Bucket> buckets = ConcurrentCollections.newConcurrentMap();
@@ -63,13 +68,6 @@ public class GoogleCloudStorageTestServer {
     /** Server endpoint **/
     private final String endpoint;
 
-    /**
-     * Creates a {@link GoogleCloudStorageTestServer} with the default endpoint
-     */
-    GoogleCloudStorageTestServer() {
-        this("https://www.googleapis.com");
-    }
-
     /**
      * Creates a {@link GoogleCloudStorageTestServer} with a custom endpoint
      */
@@ -87,29 +85,6 @@ public class GoogleCloudStorageTestServer {
         return endpoint;
     }
 
-    /**
-     * Returns a Google Cloud Storage response for the given request
-     *
-     * @param method the HTTP method of the request
-     * @param url the HTTP URL of the request
-     * @param headers the HTTP headers of the request
-     * @param body the HTTP request body
-     * @return a {@link Response}
-     *
-     * @throws IOException if something goes wrong
-     */
-    public Response handle(final String method,
-                           final String url,
-                           final Map<String, List<String>> headers,
-                           byte[] body) throws IOException {
-
-        final int questionMark = url.indexOf('?');
-        if (questionMark == -1) {
-            return handle(method, url, null, headers, body);
-        }
-        return handle(method, url.substring(0, questionMark), url.substring(questionMark + 1), headers, body);
-    }
-
     /**
      * Returns a Google Cloud Storage response for the given request
      *
@@ -165,7 +140,7 @@ public class GoogleCloudStorageTestServer {
         //
         // https://cloud.google.com/storage/docs/json_api/v1/buckets/get
         handlers.insert("GET " + endpoint + "/storage/v1/b/{bucket}", (params, headers, body) -> {
-            String name = params.get("bucket");
+            final String name = params.get("bucket");
             if (Strings.hasText(name) == false) {
                 return newError(RestStatus.INTERNAL_SERVER_ERROR, "bucket name is missing");
             }
@@ -181,7 +156,7 @@ public class GoogleCloudStorageTestServer {
         //
         // https://cloud.google.com/storage/docs/json_api/v1/objects/get
         handlers.insert("GET " + endpoint + "/storage/v1/b/{bucket}/o/{object}", (params, headers, body) -> {
-            String objectName = params.get("object");
+            final String objectName = params.get("object");
             if (Strings.hasText(objectName) == false) {
                 return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing");
             }
@@ -191,7 +166,7 @@ public class GoogleCloudStorageTestServer {
                 return newError(RestStatus.NOT_FOUND, "bucket not found");
             }
 
-            for (Map.Entry<String, byte[]> object : bucket.objects.entrySet()) {
+            for (final Map.Entry<String, byte[]> object : bucket.objects.entrySet()) {
                 if (object.getKey().equals(objectName)) {
                     return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(bucket.name, objectName, object.getValue()));
                 }
@@ -203,7 +178,7 @@ public class GoogleCloudStorageTestServer {
         //
         // https://cloud.google.com/storage/docs/json_api/v1/objects/delete
         handlers.insert("DELETE " + endpoint + "/storage/v1/b/{bucket}/o/{object}", (params, headers, body) -> {
-            String objectName = params.get("object");
+            final String objectName = params.get("object");
             if (Strings.hasText(objectName) == false) {
                 return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing");
             }
@@ -224,25 +199,149 @@ public class GoogleCloudStorageTestServer {
         //
         // https://cloud.google.com/storage/docs/json_api/v1/objects/insert
         handlers.insert("POST " + endpoint + "/upload/storage/v1/b/{bucket}/o", (params, headers, body) -> {
-            if ("resumable".equals(params.get("uploadType")) == false) {
-                return newError(RestStatus.INTERNAL_SERVER_ERROR, "upload type must be resumable");
-            }
-
-            final String objectName = params.get("name");
-            if (Strings.hasText(objectName) == false) {
-                return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing");
-            }
-
-            final Bucket bucket = buckets.get(params.get("bucket"));
-            if (bucket == null) {
-                return newError(RestStatus.NOT_FOUND, "bucket not found");
-            }
-
-            if (bucket.objects.put(objectName, EMPTY_BYTE) == null) {
-                String location = endpoint + "/upload/storage/v1/b/" + bucket.name + "/o?uploadType=resumable&upload_id=" + objectName;
-                return new Response(RestStatus.CREATED, singletonMap("Location", location), XContentType.JSON.mediaType(), EMPTY_BYTE);
+            final String uploadType = params.get("uploadType");
+            if ("resumable".equals(uploadType)) {
+                final String objectName = params.get("name");
+                if (Strings.hasText(objectName) == false) {
+                    return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing");
+                }
+                final Bucket bucket = buckets.get(params.get("bucket"));
+                if (bucket == null) {
+                    return newError(RestStatus.NOT_FOUND, "bucket not found");
+                }
+                if (bucket.objects.putIfAbsent(objectName, EMPTY_BYTE) == null) {
+                    final String location = endpoint + "/upload/storage/v1/b/" + bucket.name + "/o?uploadType=resumable&upload_id="
+                            + objectName;
+                    return new Response(RestStatus.CREATED, singletonMap("Location", location), XContentType.JSON.mediaType(), EMPTY_BYTE);
+                } else {
+                    return newError(RestStatus.CONFLICT, "object already exist");
+                }
+            } else if ("multipart".equals(uploadType)) {
+                /*
+                 *  A multipart/related request body looks like this (note the binary dump inside a text blob! nice!):
+                 * --__END_OF_PART__
+                 * Content-Length: 135
+                 * Content-Type: application/json; charset=UTF-8
+                 * content-transfer-encoding: binary
+                 *
+                 * {"bucket":"bucket_test","crc32c":"7XacHQ==","md5Hash":"fVztGkklMlUamsSmJK7W+w==",
+                 * "name":"tests-KEwE3bU4TuyetBgQIghmUw/master.dat-temp"}
+                 * --__END_OF_PART__
+                 * content-transfer-encoding: binary
+                 *
+                 * KEwE3bU4TuyetBgQIghmUw
+                 * --__END_OF_PART__--
+                 */
+                String boundary = "__END_OF_PART__";
+                // Determine the multipart boundary
+                final List<String> contentTypes = headers.getOrDefault("Content-Type", headers.get("Content-type"));
+                if (contentTypes != null) {
+                    final String contentType = contentTypes.get(0);
+                    if ((contentType != null) && contentType.contains("multipart/related; boundary=")) {
+                        boundary = contentType.replace("multipart/related; boundary=", "");
+                    }
+                }
+                InputStream inputStreamBody = new ByteArrayInputStream(body);
+                final List<String> contentEncodings = headers.getOrDefault("Content-Encoding", headers.get("Content-encoding"));
+                if (contentEncodings != null) {
+                    if (contentEncodings.stream().anyMatch(x -> "gzip".equalsIgnoreCase(x))) {
+                        inputStreamBody = new GZIPInputStream(inputStreamBody);
+                    }
+                }
+                // Read line by line ?both? parts of the multipart. Decoding headers as
+                // IS_8859_1 is safe.
+                try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreamBody, StandardCharsets.ISO_8859_1))) {
+                    String line;
+                    // read first part delimiter
+                    line = reader.readLine();
+                    if ((line == null) || (line.equals("--" + boundary) == false)) {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR,
+                                "Error parsing multipart request. Does not start with the part delimiter.");
+                    }
+                    final Map<String, List<String>> firstPartHeaders = new HashMap<>();
+                    // Reads the first part's headers, if any
+                    while ((line = reader.readLine()) != null) {
+                        if (line.equals("\r\n") || (line.length() == 0)) {
+                            // end of headers
+                            break;
+                        } else {
+                            final String[] header = line.split(":", 2);
+                            firstPartHeaders.put(header[0], singletonList(header[1]));
+                        }
+                    }
+                    final List<String> firstPartContentTypes = firstPartHeaders.getOrDefault("Content-Type",
+                            firstPartHeaders.get("Content-type"));
+                    if ((firstPartContentTypes == null)
+                            || (firstPartContentTypes.stream().noneMatch(x -> x.contains("application/json")))) {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR,
+                                "Error parsing multipart request. Metadata part expected to have the \"application/json\" content type.");
+                    }
+                    // read metadata part, a single line
+                    line = reader.readLine();
+                    final byte[] metadata = line.getBytes(StandardCharsets.ISO_8859_1);
+                    if ((firstPartContentTypes != null) && (firstPartContentTypes.stream().anyMatch((x -> x.contains("charset=utf-8"))))) {
+                        // decode as utf-8
+                        line = new String(metadata, StandardCharsets.UTF_8);
+                    }
+                    final Matcher objectNameMatcher = Pattern.compile("\"name\":\"([^\"]*)\"").matcher(line);
+                    objectNameMatcher.find();
+                    final String objectName = objectNameMatcher.group(1);
+                    final Matcher bucketNameMatcher = Pattern.compile("\"bucket\":\"([^\"]*)\"").matcher(line);
+                    bucketNameMatcher.find();
+                    final String bucketName = bucketNameMatcher.group(1);
+                    // read second part delimiter
+                    line = reader.readLine();
+                    if ((line == null) || (line.equals("--" + boundary) == false)) {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR,
+                                "Error parsing multipart request. Second part does not start with delimiter. "
+                                        + "Is the metadata multi-line?");
+                    }
+                    final Map<String, List<String>> secondPartHeaders = new HashMap<>();
+                    // Reads the second part's headers, if any
+                    while ((line = reader.readLine()) != null) {
+                        if (line.equals("\r\n") || (line.length() == 0)) {
+                            // end of headers
+                            break;
+                        } else {
+                            final String[] header = line.split(":", 2);
+                            secondPartHeaders.put(header[0], singletonList(header[1]));
+                        }
+                    }
+                    final List<String> secondPartTransferEncoding = secondPartHeaders.getOrDefault("Content-Transfer-Encoding",
+                            secondPartHeaders.get("content-transfer-encoding"));
+                    if ((secondPartTransferEncoding == null)
+                            || (secondPartTransferEncoding.stream().noneMatch(x -> x.contains("binary")))) {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR,
+                                "Error parsing multipart request. Data part expected to have the \"binary\" content transfer encoding.");
+                    }
+                    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    int c;
+                    while ((c = reader.read()) != -1) {
+                        // one char to one byte, because of the ISO_8859_1 encoding
+                        baos.write(c);
+                    }
+                    final byte[] temp = baos.toByteArray();
+                    final byte[] trailingEnding = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.ISO_8859_1);
+                    // check trailing
+                    for (int i = trailingEnding.length - 1; i >= 0; i--) {
+                        if (trailingEnding[i] != temp[(temp.length - trailingEnding.length) + i]) {
+                            return newError(RestStatus.INTERNAL_SERVER_ERROR, "Error parsing multipart request.");
+                        }
+                    }
+                    final Bucket bucket = buckets.get(bucketName);
+                    if (bucket == null) {
+                        return newError(RestStatus.NOT_FOUND, "bucket not found");
+                    }
+                    final byte[] objectData = Arrays.copyOf(temp, temp.length - trailingEnding.length);
+                    if ((objectName != null) && (bucketName != null) && (objectData != null)) {
+                        bucket.objects.put(objectName, objectData);
+                        return new Response(RestStatus.OK, emptyMap(), XContentType.JSON.mediaType(), metadata);
+                    } else {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR, "error parsing multipart request");
+                    }
+                }
             } else {
-                return newError(RestStatus.CONFLICT, "object already exist");
+                return newError(RestStatus.INTERNAL_SERVER_ERROR, "upload type must be resumable or multipart");
             }
         });
 
@@ -250,7 +349,7 @@ public class GoogleCloudStorageTestServer {
         //
         // https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload
         handlers.insert("PUT " + endpoint + "/upload/storage/v1/b/{bucket}/o", (params, headers, body) -> {
-            String objectId = params.get("upload_id");
+            final String objectId = params.get("upload_id");
             if (Strings.hasText(objectId) == false) {
                 return newError(RestStatus.INTERNAL_SERVER_ERROR, "upload id is missing");
             }
@@ -268,38 +367,46 @@ public class GoogleCloudStorageTestServer {
             return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(bucket.name, objectId, body));
         });
 
-        // Copy Object
+        // Rewrite or Copy Object
         //
+        // https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite
         // https://cloud.google.com/storage/docs/json_api/v1/objects/copy
-        handlers.insert("POST " + endpoint + "/storage/v1/b/{srcBucket}/o/{src}/copyTo/b/{destBucket}/o/{dest}", (params, headers, body)-> {
-            String source = params.get("src");
-            if (Strings.hasText(source) == false) {
-                return newError(RestStatus.INTERNAL_SERVER_ERROR, "source object name is missing");
-            }
-
-            final Bucket srcBucket = buckets.get(params.get("srcBucket"));
-            if (srcBucket == null) {
-                return newError(RestStatus.NOT_FOUND, "source bucket not found");
-            }
-
-            String dest = params.get("dest");
-            if (Strings.hasText(dest) == false) {
-                return newError(RestStatus.INTERNAL_SERVER_ERROR, "destination object name is missing");
-            }
-
-            final Bucket destBucket = buckets.get(params.get("destBucket"));
-            if (destBucket == null) {
-                return newError(RestStatus.NOT_FOUND, "destination bucket not found");
-            }
-
-            final byte[] sourceBytes = srcBucket.objects.get(source);
-            if (sourceBytes == null) {
-                return newError(RestStatus.NOT_FOUND, "source object not found");
-            }
-
-            destBucket.objects.put(dest, sourceBytes);
-            return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(destBucket.name, dest, sourceBytes));
-        });
+        handlers.insert("POST " + endpoint + "/storage/v1/b/{srcBucket}/o/{src}/{action}/b/{destBucket}/o/{dest}",
+                (params, headers, body) -> {
+                    final String action = params.get("action");
+                    if ((action.equals("rewriteTo") == false) && (action.equals("copyTo") == false)) {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR, "Action not implemented. None of \"rewriteTo\" or \"copyTo\".");
+                    }
+                    final String source = params.get("src");
+                    if (Strings.hasText(source) == false) {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR, "source object name is missing");
+                    }
+                    final Bucket srcBucket = buckets.get(params.get("srcBucket"));
+                    if (srcBucket == null) {
+                        return newError(RestStatus.NOT_FOUND, "source bucket not found");
+                    }
+                    final String dest = params.get("dest");
+                    if (Strings.hasText(dest) == false) {
+                        return newError(RestStatus.INTERNAL_SERVER_ERROR, "destination object name is missing");
+                    }
+                    final Bucket destBucket = buckets.get(params.get("destBucket"));
+                    if (destBucket == null) {
+                        return newError(RestStatus.NOT_FOUND, "destination bucket not found");
+                    }
+                    final byte[] sourceBytes = srcBucket.objects.get(source);
+                    if (sourceBytes == null) {
+                        return newError(RestStatus.NOT_FOUND, "source object not found");
+                    }
+                    destBucket.objects.put(dest, sourceBytes);
+                    if (action.equals("rewriteTo")) {
+                        final XContentBuilder respBuilder = jsonBuilder();
+                        buildRewriteResponse(respBuilder, destBucket.name, dest, sourceBytes.length);
+                        return newResponse(RestStatus.OK, emptyMap(), respBuilder);
+                    } else {
+                        assert action.equals("copyTo");
+                        return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(destBucket.name, dest, sourceBytes));
+                    }
+                });
 
         // List Objects
         //
@@ -317,8 +424,8 @@ public class GoogleCloudStorageTestServer {
                 builder.startArray("items");
 
                 final String prefixParam = params.get("prefix");
-                for (Map.Entry<String, byte[]> object : bucket.objects.entrySet()) {
-                    if (prefixParam != null && object.getKey().startsWith(prefixParam) == false) {
+                for (final Map.Entry<String, byte[]> object : bucket.objects.entrySet()) {
+                    if ((prefixParam != null) && (object.getKey().startsWith(prefixParam) == false)) {
                         continue;
                     }
                     buildObjectResource(builder, bucket.name, object.getKey(), object.getValue());
@@ -333,7 +440,7 @@ public class GoogleCloudStorageTestServer {
         //
         // https://cloud.google.com/storage/docs/request-body
         handlers.insert("GET " + endpoint + "/download/storage/v1/b/{bucket}/o/{object}", (params, headers, body) -> {
-            String object = params.get("object");
+            final String object = params.get("object");
             if (Strings.hasText(object) == false) {
                 return newError(RestStatus.INTERNAL_SERVER_ERROR, "object id is missing");
             }
@@ -353,7 +460,7 @@ public class GoogleCloudStorageTestServer {
         // Batch
         //
         // https://cloud.google.com/storage/docs/json_api/v1/how-tos/batch
-        handlers.insert("POST " + endpoint + "/batch", (params, headers, body) -> {
+        handlers.insert("POST " + endpoint + "/batch/storage/v1", (params, headers, body) -> {
             final List<Response> batchedResponses = new ArrayList<>();
 
             // A batch request body looks like this:
@@ -385,7 +492,7 @@ public class GoogleCloudStorageTestServer {
             final List<String> contentTypes = headers.getOrDefault("Content-Type", headers.get("Content-type"));
             if (contentTypes != null) {
                 final String contentType = contentTypes.get(0);
-                if (contentType != null && contentType.contains("multipart/mixed; boundary=")) {
+                if ((contentType != null) && contentType.contains("multipart/mixed; boundary=")) {
                     boundary = contentType.replace("multipart/mixed; boundary=", "");
                 }
             }
@@ -398,25 +505,25 @@ public class GoogleCloudStorageTestServer {
                 while ((line = reader.readLine()) != null) {
                     // Start of a batched request
                     if (line.equals("--" + boundary)) {
-                        Map<String, List<String>> batchedHeaders = new HashMap<>();
+                        final Map<String, List<String>> batchedHeaders = new HashMap<>();
 
                         // Reads the headers, if any
                         while ((line = reader.readLine()) != null) {
-                            if (line.equals("\r\n") || line.length() == 0) {
+                            if (line.equals("\r\n") || (line.length() == 0)) {
                                 // end of headers
                                 break;
                             } else {
-                                String[] header = line.split(":", 2);
+                                final String[] header = line.split(":", 2);
                                 batchedHeaders.put(header[0], singletonList(header[1]));
                             }
                         }
 
                         // Reads the method and URL
                         line = reader.readLine();
-                        String batchedUrl = line.substring(0, line.lastIndexOf(' '));
+                        final String batchedUrl = line.substring(0, line.lastIndexOf(' '));
 
                         final Map<String, String> batchedParams = new HashMap<>();
-                        int questionMark = batchedUrl.indexOf('?');
+                        final int questionMark = batchedUrl.indexOf('?');
                         if (questionMark != -1) {
                             RestUtils.decodeQueryString(batchedUrl.substring(questionMark + 1), 0, batchedParams);
                         }
@@ -424,16 +531,16 @@ public class GoogleCloudStorageTestServer {
                         // Reads the body
                         line = reader.readLine();
                         byte[] batchedBody = new byte[0];
-                        if (line != null || line.startsWith("--" + boundary) == false) {
+                        if ((line != null) || (line.startsWith("--" + boundary) == false)) {
                             batchedBody = line.getBytes(StandardCharsets.UTF_8);
                         }
 
                         // Executes the batched request
-                        RequestHandler handler = handlers.retrieve(batchedUrl, batchedParams);
+                        final RequestHandler handler = handlers.retrieve(batchedUrl, batchedParams);
                         if (handler != null) {
                             try {
                                 batchedResponses.add(handler.execute(batchedParams, batchedHeaders, batchedBody));
-                            } catch (IOException e) {
+                            } catch (final IOException e) {
                                 batchedResponses.add(newError(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage()));
                             }
                         }
@@ -442,11 +549,11 @@ public class GoogleCloudStorageTestServer {
             }
 
             // Now we can build the response
-            String sep = "--";
-            String line = "\r\n";
+            final String sep = "--";
+            final String line = "\r\n";
 
-            StringBuilder builder = new StringBuilder();
-            for (Response response : batchedResponses) {
+            final StringBuilder builder = new StringBuilder();
+            for (final Response response : batchedResponses) {
                 builder.append(sep).append(boundary).append(line);
                 builder.append("Content-Type: application/http").append(line);
                 builder.append(line);
@@ -465,7 +572,7 @@ public class GoogleCloudStorageTestServer {
             builder.append(line);
             builder.append(sep).append(boundary).append(sep);
 
-            byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8);
+            final byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8);
             return new Response(RestStatus.OK, emptyMap(), "multipart/mixed; boundary=" + boundary, content);
         });
 
@@ -525,7 +632,7 @@ public class GoogleCloudStorageTestServer {
         try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
             BytesReference.bytes(xContentBuilder).writeTo(out);
             return new Response(status, headers, XContentType.JSON.mediaType(), out.toByteArray());
-        } catch (IOException e) {
+        } catch (final IOException e) {
             return newError(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage());
         }
     }
@@ -552,8 +659,8 @@ public class GoogleCloudStorageTestServer {
                 BytesReference.bytes(builder).writeTo(out);
             }
             return new Response(status, emptyMap(), XContentType.JSON.mediaType(), out.toByteArray());
-        } catch (IOException e) {
-            byte[] bytes = (message != null ? message : "something went wrong").getBytes(StandardCharsets.UTF_8);
+        } catch (final IOException e) {
+            final byte[] bytes = (message != null ? message : "something went wrong").getBytes(StandardCharsets.UTF_8);
             return new Response(RestStatus.INTERNAL_SERVER_ERROR, emptyMap(), " text/plain", bytes);
         }
     }
@@ -565,6 +672,7 @@ public class GoogleCloudStorageTestServer {
     private static XContentBuilder buildBucketResource(final String name) throws IOException {
         return jsonBuilder().startObject()
                                 .field("kind", "storage#bucket")
+                                .field("name", name)
                                 .field("id", name)
                             .endObject();
     }
@@ -573,8 +681,7 @@ public class GoogleCloudStorageTestServer {
      * Storage Object JSON representation as defined in
      * https://cloud.google.com/storage/docs/json_api/v1/objects#resource
      */
-    private static XContentBuilder buildObjectResource(final String bucket, final String name, final byte[] bytes)
-            throws IOException {
+    private static XContentBuilder buildObjectResource(final String bucket, final String name, final byte[] bytes) throws IOException {
         return buildObjectResource(jsonBuilder(), bucket, name, bytes);
     }
 
@@ -590,7 +697,32 @@ public class GoogleCloudStorageTestServer {
                             .field("kind", "storage#object")
                             .field("id", String.join("/", bucket, name))
                             .field("name", name)
+                            .field("bucket", bucket)
                             .field("size", String.valueOf(bytes.length))
                         .endObject();
     }
+
+    /**
+     * Builds the rewrite response as defined by
+     * https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite
+     */
+    private static XContentBuilder buildRewriteResponse(final XContentBuilder builder,
+                                                        final String destBucket,
+                                                        final String dest,
+                                                        final int byteSize) throws IOException {
+        builder.startObject()
+                    .field("kind", "storage#rewriteResponse")
+                    .field("totalBytesRewritten", String.valueOf(byteSize))
+                    .field("objectSize", String.valueOf(byteSize))
+                    .field("done", true)
+                    .startObject("resource")
+                        .field("kind", "storage#object")
+                        .field("id", String.join("/", destBucket, dest))
+                        .field("name", dest)
+                        .field("bucket", destBucket)
+                        .field("size", String.valueOf(byteSize))
+                    .endObject()
+                .endObject();
+        return builder;
+    }
 }

+ 157 - 237
plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java

@@ -19,59 +19,55 @@
 
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.client.googleapis.batch.BatchRequest;
-import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
-import com.google.api.client.googleapis.json.GoogleJsonError;
-import com.google.api.client.googleapis.json.GoogleJsonResponseException;
-import com.google.api.client.http.HttpHeaders;
-import com.google.api.client.http.InputStreamContent;
-import com.google.api.services.storage.Storage;
-import com.google.api.services.storage.model.Bucket;
-import com.google.api.services.storage.model.Objects;
-import com.google.api.services.storage.model.StorageObject;
-import org.elasticsearch.common.Strings;
+import com.google.cloud.ReadChannel;
+import com.google.cloud.WriteChannel;
+import com.google.cloud.storage.Blob;
+import com.google.cloud.storage.BlobId;
+import com.google.cloud.storage.BlobInfo;
+import com.google.cloud.storage.Bucket;
+import com.google.cloud.storage.Storage;
+import com.google.cloud.storage.Storage.BlobListOption;
+import com.google.cloud.storage.Storage.CopyRequest;
+import org.elasticsearch.common.SuppressForbidden;
 import org.elasticsearch.common.blobstore.BlobContainer;
 import org.elasticsearch.common.blobstore.BlobMetaData;
 import org.elasticsearch.common.blobstore.BlobPath;
 import org.elasticsearch.common.blobstore.BlobStore;
 import org.elasticsearch.common.blobstore.BlobStoreException;
 import org.elasticsearch.common.blobstore.support.PlainBlobMetaData;
+import org.elasticsearch.common.collect.MapBuilder;
 import org.elasticsearch.common.component.AbstractComponent;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.util.concurrent.CountDown;
+import org.elasticsearch.core.internal.io.Streams;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 import java.nio.file.NoSuchFileException;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
 
 class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore {
 
-    /**
-     * Google Cloud Storage batch requests are limited to 1000 operations
-     **/
-    private static final int MAX_BATCHING_REQUESTS = 999;
+    // The recommended maximum size of a blob that should be uploaded in a single
+    // request. Larger files should be uploaded over multiple requests (this is
+    // called "resumable upload")
+    // https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload
+    private static final int LARGE_BLOB_THRESHOLD_BYTE_SIZE = 5 * 1024 * 1024;
 
-    private final Storage client;
+    private final Storage storage;
     private final String bucket;
 
-    GoogleCloudStorageBlobStore(Settings settings, String bucket, Storage storageClient) {
+    GoogleCloudStorageBlobStore(Settings settings, String bucket, Storage storage) {
         super(settings);
         this.bucket = bucket;
-        this.client = storageClient;
-
+        this.storage = storage;
         if (doesBucketExist(bucket) == false) {
             throw new BlobStoreException("Bucket [" + bucket + "] does not exist");
         }
@@ -99,59 +95,45 @@ class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore
      */
     boolean doesBucketExist(String bucketName) {
         try {
-            return SocketAccess.doPrivilegedIOException(() -> {
-                try {
-                    Bucket bucket = client.buckets().get(bucketName).execute();
-                    if (bucket != null) {
-                        return Strings.hasText(bucket.getId());
-                    }
-                } catch (GoogleJsonResponseException e) {
-                    GoogleJsonError error = e.getDetails();
-                    if ((e.getStatusCode() == HTTP_NOT_FOUND) || ((error != null) && (error.getCode() == HTTP_NOT_FOUND))) {
-                        return false;
-                    }
-                    throw e;
-                }
-                return false;
-            });
-        } catch (IOException e) {
+            final Bucket bucket = SocketAccess.doPrivilegedIOException(() -> storage.get(bucketName));
+            return bucket != null;
+        } catch (final Exception e) {
             throw new BlobStoreException("Unable to check if bucket [" + bucketName + "] exists", e);
         }
     }
 
     /**
-     * List all blobs in the bucket
+     * List blobs in the bucket under the specified path. The path root is removed.
      *
-     * @param path base path of the blobs to list
+     * @param path
+     *            base path of the blobs to list
      * @return a map of blob names and their metadata
      */
     Map<String, BlobMetaData> listBlobs(String path) throws IOException {
-        return SocketAccess.doPrivilegedIOException(() -> listBlobsByPath(bucket, path, path));
+        return listBlobsByPrefix(path, "");
     }
 
     /**
      * List all blobs in the bucket which have a prefix
      *
-     * @param path   base path of the blobs to list
-     * @param prefix prefix of the blobs to list
-     * @return a map of blob names and their metadata
+     * @param path
+     *            base path of the blobs to list. This path is removed from the
+     *            names of the blobs returned.
+     * @param prefix
+     *            prefix of the blobs to list.
+     * @return a map of blob names and their metadata.
      */
     Map<String, BlobMetaData> listBlobsByPrefix(String path, String prefix) throws IOException {
-        return SocketAccess.doPrivilegedIOException(() -> listBlobsByPath(bucket, buildKey(path, prefix), path));
-    }
-
-    /**
-     * Lists all blobs in a given bucket
-     *
-     * @param bucketName   name of the bucket
-     * @param path         base path of the blobs to list
-     * @param pathToRemove if true, this path part is removed from blob name
-     * @return a map of blob names and their metadata
-     */
-    private Map<String, BlobMetaData> listBlobsByPath(String bucketName, String path, String pathToRemove) throws IOException {
-        return blobsStream(client, bucketName, path, MAX_BATCHING_REQUESTS)
-                .map(new BlobMetaDataConverter(pathToRemove))
-                .collect(Collectors.toMap(PlainBlobMetaData::name, Function.identity()));
+        final String pathPrefix = buildKey(path, prefix);
+        final MapBuilder<String, BlobMetaData> mapBuilder = MapBuilder.newMapBuilder();
+        SocketAccess.doPrivilegedVoidIOException(() -> {
+            storage.get(bucket).list(BlobListOption.prefix(pathPrefix)).iterateAll().forEach(blob -> {
+                assert blob.getName().startsWith(path);
+                final String suffixName = blob.getName().substring(path.length());
+                mapBuilder.put(suffixName, new PlainBlobMetaData(suffixName, blob.getSize()));
+            });
+        });
+        return mapBuilder.immutableMap();
     }
 
     /**
@@ -161,19 +143,9 @@ class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore
      * @return true if the blob exists, false otherwise
      */
     boolean blobExists(String blobName) throws IOException {
-        try {
-            StorageObject blob = SocketAccess.doPrivilegedIOException(() -> client.objects().get(bucket, blobName).execute());
-            if (blob != null) {
-                return Strings.hasText(blob.getId());
-            }
-        } catch (GoogleJsonResponseException e) {
-            GoogleJsonError error = e.getDetails();
-            if ((e.getStatusCode() == HTTP_NOT_FOUND) || ((error != null) && (error.getCode() == HTTP_NOT_FOUND))) {
-                return false;
-            }
-            throw e;
-        }
-        return false;
+        final BlobId blobId = BlobId.of(bucket, blobName);
+        final Blob blob = SocketAccess.doPrivilegedIOException(() -> storage.get(blobId));
+        return blob != null;
     }
 
     /**
@@ -183,18 +155,29 @@ class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore
      * @return an InputStream
      */
     InputStream readBlob(String blobName) throws IOException {
-        try {
-            return SocketAccess.doPrivilegedIOException(() -> {
-                Storage.Objects.Get object = client.objects().get(bucket, blobName);
-                return object.executeMediaAsInputStream();
-            });
-        } catch (GoogleJsonResponseException e) {
-            GoogleJsonError error = e.getDetails();
-            if ((e.getStatusCode() == HTTP_NOT_FOUND) || ((error != null) && (error.getCode() == HTTP_NOT_FOUND))) {
-                throw new NoSuchFileException(e.getMessage());
-            }
-            throw e;
+        final BlobId blobId = BlobId.of(bucket, blobName);
+        final Blob blob = SocketAccess.doPrivilegedIOException(() -> storage.get(blobId));
+        if (blob == null) {
+            throw new NoSuchFileException("Blob [" + blobName + "] does not exit");
         }
+        final ReadChannel readChannel = SocketAccess.doPrivilegedIOException(blob::reader);
+        return Channels.newInputStream(new ReadableByteChannel() {
+            @SuppressForbidden(reason = "Channel is based of a socket not a file")
+            @Override
+            public int read(ByteBuffer dst) throws IOException {
+                return SocketAccess.doPrivilegedIOException(() -> readChannel.read(dst));
+            }
+
+            @Override
+            public boolean isOpen() {
+                return readChannel.isOpen();
+            }
+
+            @Override
+            public void close() throws IOException {
+                SocketAccess.doPrivilegedVoidIOException(readChannel::close);
+            }
+        });
     }
 
     /**
@@ -204,14 +187,58 @@ class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore
      * @param blobSize    expected size of the blob to be written
      */
     void writeBlob(String blobName, InputStream inputStream, long blobSize) throws IOException {
-        SocketAccess.doPrivilegedVoidIOException(() -> {
-            InputStreamContent stream = new InputStreamContent(null, inputStream);
-            stream.setLength(blobSize);
+        final BlobInfo blobInfo = BlobInfo.newBuilder(bucket, blobName).build();
+        if (blobSize > LARGE_BLOB_THRESHOLD_BYTE_SIZE) {
+            writeBlobResumable(blobInfo, inputStream);
+        } else {
+            writeBlobMultipart(blobInfo, inputStream, blobSize);
+        }
+    }
 
-            Storage.Objects.Insert insert = client.objects().insert(bucket, null, stream);
-            insert.setName(blobName);
-            insert.execute();
-        });
+    /**
+     * Uploads a blob using the "resumable upload" method (multiple requests, which
+     * can be independently retried in case of failure, see
+     * https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload
+     *
+     * @param blobInfo the info for the blob to be uploaded
+     * @param inputStream the stream containing the blob data
+     */
+    private void writeBlobResumable(BlobInfo blobInfo, InputStream inputStream) throws IOException {
+        final WriteChannel writeChannel = SocketAccess.doPrivilegedIOException(() -> storage.writer(blobInfo));
+        Streams.copy(inputStream, Channels.newOutputStream(new WritableByteChannel() {
+            @Override
+            public boolean isOpen() {
+                return writeChannel.isOpen();
+            }
+
+            @Override
+            public void close() throws IOException {
+                SocketAccess.doPrivilegedVoidIOException(writeChannel::close);
+            }
+
+            @SuppressForbidden(reason = "Channel is based of a socket not a file")
+            @Override
+            public int write(ByteBuffer src) throws IOException {
+                return SocketAccess.doPrivilegedIOException(() -> writeChannel.write(src));
+            }
+        }));
+    }
+
+    /**
+     * Uploads a blob using the "multipart upload" method (a single
+     * 'multipart/related' request containing both data and metadata. The request is
+     * gziped), see:
+     * https://cloud.google.com/storage/docs/json_api/v1/how-tos/multipart-upload
+     *
+     * @param blobInfo the info for the blob to be uploaded
+     * @param inputStream the stream containing the blob data
+     * @param blobSize the size
+     */
+    private void writeBlobMultipart(BlobInfo blobInfo, InputStream inputStream, long blobSize) throws IOException {
+        assert blobSize <= LARGE_BLOB_THRESHOLD_BYTE_SIZE : "large blob uploads should use the resumable upload method";
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(Math.toIntExact(blobSize));
+        Streams.copy(inputStream, baos);
+        SocketAccess.doPrivilegedVoidIOException(() -> storage.create(blobInfo, baos.toByteArray()));
     }
 
     /**
@@ -220,10 +247,11 @@ class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore
      * @param blobName name of the blob
      */
     void deleteBlob(String blobName) throws IOException {
-        if (!blobExists(blobName)) {
+        final BlobId blobId = BlobId.of(bucket, blobName);
+        final boolean deleted = SocketAccess.doPrivilegedIOException(() -> storage.delete(blobId));
+        if (deleted == false) {
             throw new NoSuchFileException("Blob [" + blobName + "] does not exist");
         }
-        SocketAccess.doPrivilegedIOException(() -> client.objects().delete(bucket, blobName).execute());
     }
 
     /**
@@ -232,7 +260,7 @@ class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore
      * @param prefix prefix of the buckets to delete
      */
     void deleteBlobsByPrefix(String prefix) throws IOException {
-        deleteBlobs(listBlobsByPath(bucket, prefix, null).keySet());
+        deleteBlobs(listBlobsByPrefix("", prefix).keySet());
     }
 
     /**
@@ -241,163 +269,55 @@ class GoogleCloudStorageBlobStore extends AbstractComponent implements BlobStore
      * @param blobNames names of the bucket to delete
      */
     void deleteBlobs(Collection<String> blobNames) throws IOException {
-        if (blobNames == null || blobNames.isEmpty()) {
+        if (blobNames.isEmpty()) {
             return;
         }
-
+        // for a single op submit a simple delete instead of a batch of size 1
         if (blobNames.size() == 1) {
             deleteBlob(blobNames.iterator().next());
             return;
         }
-        final List<Storage.Objects.Delete> deletions = new ArrayList<>(Math.min(MAX_BATCHING_REQUESTS, blobNames.size()));
-        final Iterator<String> blobs = blobNames.iterator();
-
-        SocketAccess.doPrivilegedVoidIOException(() -> {
-            while (blobs.hasNext()) {
-                // Create a delete request for each blob to delete
-                deletions.add(client.objects().delete(bucket, blobs.next()));
-
-                if (blobs.hasNext() == false || deletions.size() == MAX_BATCHING_REQUESTS) {
-                    try {
-                        // Deletions are executed using a batch request
-                        BatchRequest batch = client.batch();
-
-                        // Used to track successful deletions
-                        CountDown countDown = new CountDown(deletions.size());
-
-                        for (Storage.Objects.Delete delete : deletions) {
-                            // Queue the delete request in batch
-                            delete.queue(batch, new JsonBatchCallback<Void>() {
-                                @Override
-                                public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) throws IOException {
-                                    logger.error("failed to delete blob [{}] in bucket [{}]: {}", delete.getObject(), delete.getBucket(), e
-                                        .getMessage());
-                                }
-
-                                @Override
-                                public void onSuccess(Void aVoid, HttpHeaders responseHeaders) throws IOException {
-                                    countDown.countDown();
-                                }
-                            });
-                        }
-
-                        batch.execute();
-
-                        if (countDown.isCountedDown() == false) {
-                            throw new IOException("Failed to delete all [" + deletions.size() + "] blobs");
-                        }
-                    } finally {
-                        deletions.clear();
-                    }
-                }
+        final List<BlobId> blobIdsToDelete = blobNames.stream().map(blobName -> BlobId.of(bucket, blobName)).collect(Collectors.toList());
+        final List<Boolean> deletedStatuses = SocketAccess.doPrivilegedIOException(() -> storage.delete(blobIdsToDelete));
+        assert blobIdsToDelete.size() == deletedStatuses.size();
+        boolean failed = false;
+        for (int i = 0; i < blobIdsToDelete.size(); i++) {
+            if (deletedStatuses.get(i) == false) {
+                logger.error("Failed to delete blob [{}] in bucket [{}]", blobIdsToDelete.get(i).getName(), bucket);
+                failed = true;
             }
-        });
+        }
+        if (failed) {
+            throw new IOException("Failed to delete all [" + blobIdsToDelete.size() + "] blobs");
+        }
     }
 
     /**
      * Moves a blob within the same bucket
      *
      * @param sourceBlob name of the blob to move
-     * @param targetBlob new name of the blob in the target bucket
+     * @param targetBlob new name of the blob in the same bucket
      */
-    void moveBlob(String sourceBlob, String targetBlob) throws IOException {
-        SocketAccess.doPrivilegedIOException(() -> {
+    void moveBlob(String sourceBlobName, String targetBlobName) throws IOException {
+        final BlobId sourceBlobId = BlobId.of(bucket, sourceBlobName);
+        final BlobId targetBlobId = BlobId.of(bucket, targetBlobName);
+        final CopyRequest request = CopyRequest.newBuilder()
+                .setSource(sourceBlobId)
+                .setTarget(targetBlobId)
+                .build();
+        SocketAccess.doPrivilegedVoidIOException(() -> {
             // There's no atomic "move" in GCS so we need to copy and delete
-            client.objects().copy(bucket, sourceBlob, bucket, targetBlob, null).execute();
-            client.objects().delete(bucket, sourceBlob).execute();
-            return null;
+            storage.copy(request).getResult();
+            final boolean deleted = storage.delete(sourceBlobId);
+            if (deleted == false) {
+                throw new IOException("Failed to move source [" + sourceBlobName + "] to target [" + targetBlobName + "]");
+            }
         });
     }
 
-    private String buildKey(String keyPath, String s) {
+    private static String buildKey(String keyPath, String s) {
         assert s != null;
         return keyPath + s;
     }
 
-    /**
-     * Converts a {@link StorageObject} to a {@link PlainBlobMetaData}
-     */
-    class BlobMetaDataConverter implements Function<StorageObject, PlainBlobMetaData> {
-
-        private final String pathToRemove;
-
-        BlobMetaDataConverter(String pathToRemove) {
-            this.pathToRemove = pathToRemove;
-        }
-
-        @Override
-        public PlainBlobMetaData apply(StorageObject storageObject) {
-            String blobName = storageObject.getName();
-            if (Strings.hasLength(pathToRemove)) {
-                blobName = blobName.substring(pathToRemove.length());
-            }
-            return new PlainBlobMetaData(blobName, storageObject.getSize().longValue());
-        }
-    }
-
-    /**
-     * Spliterator can be used to list storage objects stored in a bucket.
-     */
-    static class StorageObjectsSpliterator implements Spliterator<StorageObject> {
-
-        private final Storage.Objects.List list;
-
-        StorageObjectsSpliterator(Storage client, String bucketName, String prefix, long pageSize) throws IOException {
-            list = SocketAccess.doPrivilegedIOException(() -> client.objects().list(bucketName));
-            list.setMaxResults(pageSize);
-            if (prefix != null) {
-                list.setPrefix(prefix);
-            }
-        }
-
-        @Override
-        public boolean tryAdvance(Consumer<? super StorageObject> action) {
-            try {
-                // Retrieves the next page of items
-                Objects objects = SocketAccess.doPrivilegedIOException(list::execute);
-
-                if ((objects == null) || (objects.getItems() == null) || (objects.getItems().isEmpty())) {
-                    return false;
-                }
-
-                // Consumes all the items
-                objects.getItems().forEach(action::accept);
-
-                // Sets the page token of the next page,
-                // null indicates that all items have been consumed
-                String next = objects.getNextPageToken();
-                if (next != null) {
-                    list.setPageToken(next);
-                    return true;
-                }
-
-                return false;
-            } catch (Exception e) {
-                throw new BlobStoreException("Exception while listing objects", e);
-            }
-        }
-
-        @Override
-        public Spliterator<StorageObject> trySplit() {
-            return null;
-        }
-
-        @Override
-        public long estimateSize() {
-            return Long.MAX_VALUE;
-        }
-
-        @Override
-        public int characteristics() {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns a {@link Stream} of {@link StorageObject}s that are stored in a given bucket.
-     */
-    static Stream<StorageObject> blobsStream(Storage client, String bucketName, String prefix, long pageSize) throws IOException {
-        return StreamSupport.stream(new StorageObjectsSpliterator(client, bucketName, prefix, pageSize), false);
-    }
-
 }

+ 69 - 31
plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageClientSettings.java

@@ -18,8 +18,10 @@
  */
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
 import com.google.api.services.storage.StorageScopes;
+import com.google.auth.oauth2.ServiceAccountCredentials;
+
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.SecureSetting;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
@@ -28,10 +30,12 @@ import org.elasticsearch.common.unit.TimeValue;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
+import java.net.URI;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Locale;
 import java.util.Map;
+import java.util.function.Function;
 
 import static org.elasticsearch.common.settings.Setting.timeSetting;
 
@@ -44,11 +48,19 @@ public class GoogleCloudStorageClientSettings {
 
     /** A json Service Account file loaded from secure settings. */
     static final Setting.AffixSetting<InputStream> CREDENTIALS_FILE_SETTING = Setting.affixKeySetting(PREFIX, "credentials_file",
-        key -> SecureSetting.secureFile(key, null));
+            key -> SecureSetting.secureFile(key, null));
 
     /** An override for the Storage endpoint to connect to. */
     static final Setting.AffixSetting<String> ENDPOINT_SETTING = Setting.affixKeySetting(PREFIX, "endpoint",
-        key -> new Setting<>(key, "", s -> s, Setting.Property.NodeScope));
+            key -> Setting.simpleString(key, Setting.Property.NodeScope));
+
+    /** An override for the Google Project ID. */
+    static final Setting.AffixSetting<String> PROJECT_ID_SETTING = Setting.affixKeySetting(PREFIX, "project_id",
+            key -> Setting.simpleString(key, Setting.Property.NodeScope));
+
+    /** An override for the Token Server URI in the oauth flow. */
+    static final Setting.AffixSetting<URI> TOKEN_URI_SETTING = Setting.affixKeySetting(PREFIX, "token_uri",
+            key -> new Setting<>(key, "", URI::create, Setting.Property.NodeScope));
 
     /**
      * The timeout to establish a connection. A value of {@code -1} corresponds to an infinite timeout. A value of {@code 0}
@@ -64,45 +76,59 @@ public class GoogleCloudStorageClientSettings {
     static final Setting.AffixSetting<TimeValue> READ_TIMEOUT_SETTING = Setting.affixKeySetting(PREFIX, "read_timeout",
         key -> timeSetting(key, TimeValue.ZERO, TimeValue.MINUS_ONE, Setting.Property.NodeScope));
 
-    /** Name used by the client when it uses the Google Cloud JSON API. **/
+    /** Name used by the client when it uses the Google Cloud JSON API. */
     static final Setting.AffixSetting<String> APPLICATION_NAME_SETTING = Setting.affixKeySetting(PREFIX, "application_name",
-        key -> new Setting<>(key, "repository-gcs", s -> s, Setting.Property.NodeScope));
+        key -> new Setting<>(key, "repository-gcs", Function.identity(), Setting.Property.NodeScope, Setting.Property.Deprecated));
 
-    /** The credentials used by the client to connect to the Storage endpoint **/
-    private final GoogleCredential credential;
+    /** The credentials used by the client to connect to the Storage endpoint. */
+    private final ServiceAccountCredentials credential;
 
-    /** The Storage root URL the client should talk to, or empty string to use the default. **/
+    /** The Storage endpoint URL the client should talk to. Null value sets the default. */
     private final String endpoint;
 
-    /** The timeout to establish a connection **/
+    /** The Google project ID overriding the default way to infer it. Null value sets the default. */
+    private final String projectId;
+
+    /** The timeout to establish a connection */
     private final TimeValue connectTimeout;
 
-    /** The timeout to read data from an established connection **/
+    /** The timeout to read data from an established connection */
     private final TimeValue readTimeout;
 
-    /** The Storage client application name **/
+    /** The Storage client application name */
     private final String applicationName;
 
-    GoogleCloudStorageClientSettings(final GoogleCredential credential,
+    /** The token server URI. This leases access tokens in the oauth flow. */
+    private final URI tokenUri;
+
+    GoogleCloudStorageClientSettings(final ServiceAccountCredentials credential,
                                      final String endpoint,
+                                     final String projectId,
                                      final TimeValue connectTimeout,
                                      final TimeValue readTimeout,
-                                     final String applicationName) {
+                                     final String applicationName,
+                                     final URI tokenUri) {
         this.credential = credential;
         this.endpoint = endpoint;
+        this.projectId = projectId;
         this.connectTimeout = connectTimeout;
         this.readTimeout = readTimeout;
         this.applicationName = applicationName;
+        this.tokenUri = tokenUri;
     }
 
-    public GoogleCredential getCredential() {
+    public ServiceAccountCredentials getCredential() {
         return credential;
     }
 
-    public String getEndpoint() {
+    public String getHost() {
         return endpoint;
     }
 
+    public String getProjectId() {
+        return Strings.hasLength(projectId) ? projectId : (credential != null ? credential.getProjectId() : null);
+    }
+
     public TimeValue getConnectTimeout() {
         return connectTimeout;
     }
@@ -115,9 +141,13 @@ public class GoogleCloudStorageClientSettings {
         return applicationName;
     }
 
+    public URI getTokenUri() {
+        return tokenUri;
+    }
+
     public static Map<String, GoogleCloudStorageClientSettings> load(final Settings settings) {
         final Map<String, GoogleCloudStorageClientSettings> clients = new HashMap<>();
-        for (String clientName: settings.getGroups(PREFIX).keySet()) {
+        for (final String clientName: settings.getGroups(PREFIX).keySet()) {
             clients.put(clientName, getClientSettings(settings, clientName));
         }
         if (clients.containsKey("default") == false) {
@@ -132,22 +162,27 @@ public class GoogleCloudStorageClientSettings {
         return new GoogleCloudStorageClientSettings(
             loadCredential(settings, clientName),
             getConfigValue(settings, clientName, ENDPOINT_SETTING),
+            getConfigValue(settings, clientName, PROJECT_ID_SETTING),
             getConfigValue(settings, clientName, CONNECT_TIMEOUT_SETTING),
             getConfigValue(settings, clientName, READ_TIMEOUT_SETTING),
-            getConfigValue(settings, clientName, APPLICATION_NAME_SETTING)
+            getConfigValue(settings, clientName, APPLICATION_NAME_SETTING),
+            getConfigValue(settings, clientName, TOKEN_URI_SETTING)
         );
     }
 
     /**
-     * Loads the service account file corresponding to a given client name. If no file is defined for the client,
-     * a {@code null} credential is returned.
+     * Loads the service account file corresponding to a given client name. If no
+     * file is defined for the client, a {@code null} credential is returned.
      *
-     * @param settings the {@link Settings}
-     * @param clientName the client name
+     * @param settings
+     *            the {@link Settings}
+     * @param clientName
+     *            the client name
      *
-     * @return the {@link GoogleCredential} to use for the given client, {@code null} if no service account is defined.
+     * @return the {@link ServiceAccountCredentials} to use for the given client,
+     *         {@code null} if no service account is defined.
      */
-    static GoogleCredential loadCredential(final Settings settings, final String clientName) {
+    static ServiceAccountCredentials loadCredential(final Settings settings, final String clientName) {
         try {
             if (CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace(clientName).exists(settings) == false) {
                 // explicitly returning null here so that the default credential
@@ -155,19 +190,22 @@ public class GoogleCloudStorageClientSettings {
                 return null;
             }
             try (InputStream credStream = CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace(clientName).get(settings)) {
-                GoogleCredential credential = GoogleCredential.fromStream(credStream);
-                if (credential.createScopedRequired()) {
-                    credential = credential.createScoped(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL));
-                }
-                return credential;
+                final Collection<String> scopes = Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL);
+                return SocketAccess.doPrivilegedIOException(() -> {
+                    final ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream(credStream);
+                    if (credentials.createScopedRequired()) {
+                        return (ServiceAccountCredentials) credentials.createScoped(scopes);
+                    }
+                    return credentials;
+                });
             }
-        } catch (IOException e) {
+        } catch (final IOException e) {
             throw new UncheckedIOException(e);
         }
     }
 
     private static <T> T getConfigValue(final Settings settings, final String clientName, final Setting.AffixSetting<T> clientSetting) {
-        Setting<T> concreteSetting = clientSetting.getConcreteSettingForNamespace(clientName);
+        final Setting<T> concreteSetting = clientSetting.getConcreteSettingForNamespace(clientName);
         return concreteSetting.get(settings);
     }
 }

+ 3 - 75
plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java

@@ -19,21 +19,6 @@
 
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.client.auth.oauth2.TokenRequest;
-import com.google.api.client.auth.oauth2.TokenResponse;
-import com.google.api.client.googleapis.json.GoogleJsonError;
-import com.google.api.client.http.GenericUrl;
-import com.google.api.client.http.HttpHeaders;
-import com.google.api.client.json.GenericJson;
-import com.google.api.client.json.webtoken.JsonWebSignature;
-import com.google.api.client.json.webtoken.JsonWebToken;
-import com.google.api.client.util.ClassInfo;
-import com.google.api.client.util.Data;
-import com.google.api.services.storage.Storage;
-import com.google.api.services.storage.model.Bucket;
-import com.google.api.services.storage.model.Objects;
-import com.google.api.services.storage.model.StorageObject;
-import org.elasticsearch.SpecialPermission;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.NamedXContentRegistry;
@@ -42,8 +27,6 @@ import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.plugins.RepositoryPlugin;
 import org.elasticsearch.repositories.Repository;
 
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -51,63 +34,6 @@ import java.util.Map;
 
 public class GoogleCloudStoragePlugin extends Plugin implements RepositoryPlugin {
 
-    static {
-        /*
-         * Google HTTP client changes access levels because its silly and we
-         * can't allow that on any old stack stack so we pull it here, up front,
-         * so we can cleanly check the permissions for it. Without this changing
-         * the permission can fail if any part of core is on the stack because
-         * our plugin permissions don't allow core to "reach through" plugins to
-         * change the permission. Because that'd be silly.
-         */
-        SpecialPermission.check();
-        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
-            // ClassInfo put in cache all the fields of a given class
-            // that are annoted with @Key; at the same time it changes
-            // the field access level using setAccessible(). Calling
-            // them here put the ClassInfo in cache (they are never evicted)
-            // before the SecurityManager is installed.
-            ClassInfo.of(HttpHeaders.class, true);
-
-            ClassInfo.of(JsonWebSignature.Header.class, false);
-            ClassInfo.of(JsonWebToken.Payload.class, false);
-
-            ClassInfo.of(TokenRequest.class, false);
-            ClassInfo.of(TokenResponse.class, false);
-
-            ClassInfo.of(GenericJson.class, false);
-            ClassInfo.of(GenericUrl.class, false);
-
-            Data.nullOf(GoogleJsonError.ErrorInfo.class);
-            ClassInfo.of(GoogleJsonError.class, false);
-
-            Data.nullOf(Bucket.Cors.class);
-            ClassInfo.of(Bucket.class, false);
-            ClassInfo.of(Bucket.Cors.class, false);
-            ClassInfo.of(Bucket.Lifecycle.class, false);
-            ClassInfo.of(Bucket.Logging.class, false);
-            ClassInfo.of(Bucket.Owner.class, false);
-            ClassInfo.of(Bucket.Versioning.class, false);
-            ClassInfo.of(Bucket.Website.class, false);
-
-            ClassInfo.of(StorageObject.class, false);
-            ClassInfo.of(StorageObject.Owner.class, false);
-
-            ClassInfo.of(Objects.class, false);
-
-            ClassInfo.of(Storage.Buckets.Get.class, false);
-            ClassInfo.of(Storage.Buckets.Insert.class, false);
-
-            ClassInfo.of(Storage.Objects.Get.class, false);
-            ClassInfo.of(Storage.Objects.Insert.class, false);
-            ClassInfo.of(Storage.Objects.Delete.class, false);
-            ClassInfo.of(Storage.Objects.Copy.class, false);
-            ClassInfo.of(Storage.Objects.List.class, false);
-
-            return null;
-        });
-    }
-
     private final Map<String, GoogleCloudStorageClientSettings> clientsSettings;
 
     public GoogleCloudStoragePlugin(final Settings settings) {
@@ -134,8 +60,10 @@ public class GoogleCloudStoragePlugin extends Plugin implements RepositoryPlugin
         return Arrays.asList(
             GoogleCloudStorageClientSettings.CREDENTIALS_FILE_SETTING,
             GoogleCloudStorageClientSettings.ENDPOINT_SETTING,
+            GoogleCloudStorageClientSettings.PROJECT_ID_SETTING,
             GoogleCloudStorageClientSettings.CONNECT_TIMEOUT_SETTING,
             GoogleCloudStorageClientSettings.READ_TIMEOUT_SETTING,
-            GoogleCloudStorageClientSettings.APPLICATION_NAME_SETTING);
+            GoogleCloudStorageClientSettings.APPLICATION_NAME_SETTING,
+            GoogleCloudStorageClientSettings.TOKEN_URI_SETTING);
     }
 }

+ 2 - 3
plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.services.storage.Storage;
 import org.elasticsearch.cluster.metadata.RepositoryMetaData;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.blobstore.BlobPath;
@@ -27,7 +26,6 @@ import org.elasticsearch.common.blobstore.BlobStore;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.unit.ByteSizeUnit;
 import org.elasticsearch.common.unit.ByteSizeValue;
-import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.xcontent.NamedXContentRegistry;
 import org.elasticsearch.env.Environment;
 import org.elasticsearch.repositories.RepositoryException;
@@ -39,7 +37,8 @@ import static org.elasticsearch.common.settings.Setting.Property;
 import static org.elasticsearch.common.settings.Setting.boolSetting;
 import static org.elasticsearch.common.settings.Setting.byteSizeSetting;
 import static org.elasticsearch.common.settings.Setting.simpleString;
-import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;
+
+import com.google.cloud.storage.Storage;
 
 class GoogleCloudStorageRepository extends BlobStoreRepository {
 

+ 98 - 77
plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageService.java

@@ -19,23 +19,26 @@
 
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
-import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
-import com.google.api.client.http.HttpBackOffIOExceptionHandler;
-import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler;
-import com.google.api.client.http.HttpRequest;
-import com.google.api.client.http.HttpRequestInitializer;
+import com.google.api.client.googleapis.GoogleUtils;
 import com.google.api.client.http.HttpTransport;
-import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
-import com.google.api.client.json.jackson2.JacksonFactory;
-import com.google.api.client.util.ExponentialBackOff;
-import com.google.api.services.storage.Storage;
+import com.google.api.client.http.javanet.DefaultConnectionFactory;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.auth.oauth2.ServiceAccountCredentials;
+import com.google.cloud.http.HttpTransportOptions;
+import com.google.cloud.storage.Storage;
+import com.google.cloud.storage.StorageOptions;
+
 import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.collect.MapBuilder;
 import org.elasticsearch.common.component.AbstractComponent;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.env.Environment;
 
 import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.Map;
 
 public class GoogleCloudStorageService extends AbstractComponent {
@@ -51,42 +54,107 @@ public class GoogleCloudStorageService extends AbstractComponent {
     /**
      * Creates a client that can be used to manage Google Cloud Storage objects.
      *
-     * @param clientName name of client settings to use from secure settings
+     * @param clientName name of client settings to use, including secure settings
      * @return a Client instance that can be used to manage Storage objects
      */
     public Storage createClient(final String clientName) throws Exception {
         final GoogleCloudStorageClientSettings clientSettings = clientsSettings.get(clientName);
         if (clientSettings == null) {
-            throw new IllegalArgumentException("Unknown client name [" + clientName + "]. Existing client configs: " +
-                Strings.collectionToDelimitedString(clientsSettings.keySet(), ","));
+            throw new IllegalArgumentException("Unknown client name [" + clientName + "]. Existing client configs: "
+                    + Strings.collectionToDelimitedString(clientsSettings.keySet(), ","));
         }
-
-        HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();
-        HttpRequestInitializer requestInitializer =  createRequestInitializer(clientSettings);
-
-        Storage.Builder storage = new Storage.Builder(transport, JacksonFactory.getDefaultInstance(), requestInitializer);
-        if (Strings.hasLength(clientSettings.getApplicationName())) {
-            storage.setApplicationName(clientSettings.getApplicationName());
+        final HttpTransport httpTransport = createHttpTransport(clientSettings.getHost());
+        final HttpTransportOptions httpTransportOptions = HttpTransportOptions.newBuilder()
+                .setConnectTimeout(toTimeout(clientSettings.getConnectTimeout()))
+                .setReadTimeout(toTimeout(clientSettings.getReadTimeout()))
+                .setHttpTransportFactory(() -> httpTransport)
+                .build();
+        final StorageOptions.Builder storageOptionsBuilder = StorageOptions.newBuilder()
+                .setTransportOptions(httpTransportOptions)
+                .setHeaderProvider(() -> {
+                    final MapBuilder<String, String> mapBuilder = MapBuilder.newMapBuilder();
+                    if (Strings.hasLength(clientSettings.getApplicationName())) {
+                        mapBuilder.put("user-agent", clientSettings.getApplicationName());
+                    }
+                    return mapBuilder.immutableMap();
+                });
+        if (Strings.hasLength(clientSettings.getHost())) {
+            storageOptionsBuilder.setHost(clientSettings.getHost());
         }
-        if (Strings.hasLength(clientSettings.getEndpoint())) {
-            storage.setRootUrl(clientSettings.getEndpoint());
+        if (Strings.hasLength(clientSettings.getProjectId())) {
+            storageOptionsBuilder.setProjectId(clientSettings.getProjectId());
         }
-        return storage.build();
+        if (clientSettings.getCredential() == null) {
+            logger.warn("\"Application Default Credentials\" are not supported out of the box."
+                    + " Additional file system permissions have to be granted to the plugin.");
+        } else {
+            ServiceAccountCredentials serviceAccountCredentials = clientSettings.getCredential();
+            // override token server URI
+            final URI tokenServerUri = clientSettings.getTokenUri();
+            if (Strings.hasLength(tokenServerUri.toString())) {
+                // Rebuild the service account credentials in order to use a custom Token url.
+                // This is mostly used for testing purpose.
+                serviceAccountCredentials = serviceAccountCredentials.toBuilder().setTokenServerUri(tokenServerUri).build();
+            }
+            storageOptionsBuilder.setCredentials(serviceAccountCredentials);
+        }
+        return storageOptionsBuilder.build().getService();
     }
 
-    static HttpRequestInitializer createRequestInitializer(final GoogleCloudStorageClientSettings settings) throws IOException {
-        GoogleCredential credential = settings.getCredential();
-        if (credential == null) {
-            credential = GoogleCredential.getApplicationDefault();
+    /**
+     * Pins the TLS trust certificates and, more importantly, overrides connection
+     * URLs in the case of a custom endpoint setting because some connections don't
+     * fully honor this setting (bugs in the SDK). The default connection factory
+     * opens a new connection for each request. This is required for the storage
+     * instance to be thread-safe.
+     **/
+    private static HttpTransport createHttpTransport(final String endpoint) throws Exception {
+        final NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
+        // requires java.lang.RuntimePermission "setFactory"
+        builder.trustCertificates(GoogleUtils.getCertificateTrustStore());
+        if (Strings.hasLength(endpoint)) {
+            final URL endpointUrl = URI.create(endpoint).toURL();
+            builder.setConnectionFactory(new DefaultConnectionFactory() {
+                @Override
+                public HttpURLConnection openConnection(final URL originalUrl) throws IOException {
+                    // test if the URL is built correctly, ie following the `host` setting
+                    if (originalUrl.getHost().equals(endpointUrl.getHost()) && originalUrl.getPort() == endpointUrl.getPort()
+                            && originalUrl.getProtocol().equals(endpointUrl.getProtocol())) {
+                        return super.openConnection(originalUrl);
+                    }
+                    // override connection URLs because some don't follow the config. See
+                    // https://github.com/GoogleCloudPlatform/google-cloud-java/issues/3254 and
+                    // https://github.com/GoogleCloudPlatform/google-cloud-java/issues/3255
+                    URI originalUri;
+                    try {
+                        originalUri = originalUrl.toURI();
+                    } catch (final URISyntaxException e) {
+                        throw new RuntimeException(e);
+                    }
+                    String overridePath = "/";
+                    if (originalUri.getRawPath() != null) {
+                        overridePath = originalUri.getRawPath();
+                    }
+                    if (originalUri.getRawQuery() != null) {
+                        overridePath += "?" + originalUri.getRawQuery();
+                    }
+                    return super.openConnection(
+                            new URL(endpointUrl.getProtocol(), endpointUrl.getHost(), endpointUrl.getPort(), overridePath));
+                }
+            });
         }
-        return new DefaultHttpRequestInitializer(credential, toTimeout(settings.getConnectTimeout()), toTimeout(settings.getReadTimeout()));
+        return builder.build();
     }
 
-    /** Converts timeout values from the settings to a timeout value for the Google Cloud SDK **/
+    /**
+     * Converts timeout values from the settings to a timeout value for the Google
+     * Cloud SDK
+     **/
     static Integer toTimeout(final TimeValue timeout) {
         // Null or zero in settings means the default timeout
         if (timeout == null || TimeValue.ZERO.equals(timeout)) {
-            return null;
+            // negative value means using the default value
+            return -1;
         }
         // -1 means infinite timeout
         if (TimeValue.MINUS_ONE.equals(timeout)) {
@@ -96,51 +164,4 @@ public class GoogleCloudStorageService extends AbstractComponent {
         return Math.toIntExact(timeout.getMillis());
     }
 
-    /**
-     * HTTP request initializer that set timeouts and backoff handler while deferring authentication to GoogleCredential.
-     * See https://cloud.google.com/storage/transfer/create-client#retry
-     */
-    static class DefaultHttpRequestInitializer implements HttpRequestInitializer {
-
-        private final Integer connectTimeout;
-        private final Integer readTimeout;
-        private final GoogleCredential credential;
-
-        DefaultHttpRequestInitializer(GoogleCredential credential, Integer connectTimeoutMillis, Integer readTimeoutMillis) {
-            this.credential = credential;
-            this.connectTimeout = connectTimeoutMillis;
-            this.readTimeout = readTimeoutMillis;
-        }
-
-        @Override
-        public void initialize(HttpRequest request) {
-            if (connectTimeout != null) {
-                request.setConnectTimeout(connectTimeout);
-            }
-            if (readTimeout != null) {
-                request.setReadTimeout(readTimeout);
-            }
-
-            request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(newBackOff()));
-            request.setInterceptor(credential);
-
-            final HttpUnsuccessfulResponseHandler handler = new HttpBackOffUnsuccessfulResponseHandler(newBackOff());
-            request.setUnsuccessfulResponseHandler((req, resp, supportsRetry) -> {
-                    // Let the credential handle the response. If it failed, we rely on our backoff handler
-                    return credential.handleResponse(req, resp, supportsRetry) || handler.handleResponse(req, resp, supportsRetry);
-                }
-            );
-        }
-
-        private ExponentialBackOff newBackOff() {
-            return new ExponentialBackOff.Builder()
-                    .setInitialIntervalMillis(100)
-                    .setMaxIntervalMillis(6000)
-                    .setMaxElapsedTimeMillis(900000)
-                    .setMultiplier(1.5)
-                    .setRandomizationFactor(0.5)
-                    .build();
-        }
-    }
-
 }

+ 4 - 3
plugins/repository-gcs/src/main/plugin-metadata/plugin-security.policy

@@ -18,11 +18,12 @@
  */
 
 grant {
+  // required by: com.google.api.client.json.JsonParser#parseValue
   permission java.lang.RuntimePermission "accessDeclaredMembers";
-  permission java.lang.RuntimePermission "setFactory";
+  // required by: com.google.api.client.json.GenericJson#<init>
   permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
-  permission java.net.URLPermission "http://www.googleapis.com/*", "*";
-  permission java.net.URLPermission "https://www.googleapis.com/*", "*";
+  // required to add google certs to the gcs client trustore
+  permission java.lang.RuntimePermission "setFactory";
 
   // gcs client opens socket connections for to access repository
   permission java.net.SocketPermission "*", "connect";

+ 54 - 0
plugins/repository-gcs/src/test/java/com/google/cloud/storage/StorageRpcOptionUtils.java

@@ -0,0 +1,54 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.google.cloud.storage;
+
+import com.google.cloud.storage.spi.v1.StorageRpc;
+
+import static org.mockito.Mockito.mock;
+
+/**
+ * Utility class that exposed Google SDK package protected methods to
+ * create specific StorageRpc objects in unit tests.
+ */
+public class StorageRpcOptionUtils {
+
+    private StorageRpcOptionUtils(){}
+
+    public static String getPrefix(final Storage.BlobListOption... options) {
+        if (options != null) {
+            for (final Option option : options) {
+                final StorageRpc.Option rpcOption = option.getRpcOption();
+                if (StorageRpc.Option.PREFIX.equals(rpcOption)) {
+                    return (String) option.getValue();
+                }
+            }
+        }
+        return null;
+    }
+
+    public static CopyWriter createCopyWriter(final Blob result) {
+        return new CopyWriter(mock(StorageOptions.class), mock(StorageRpc.RewriteResponse.class)) {
+            @Override
+            public Blob getResult() {
+                return result;
+            }
+        };
+    }
+}

+ 37 - 0
plugins/repository-gcs/src/test/java/com/google/cloud/storage/StorageTestUtils.java

@@ -0,0 +1,37 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.google.cloud.storage;
+
+/**
+ * Utility class that exposed Google SDK package protected methods to
+ * create buckets and blobs objects in unit tests.
+ */
+public class StorageTestUtils {
+
+    private StorageTestUtils(){}
+
+    public static Bucket createBucket(final Storage storage, final String bucketName) {
+        return new Bucket(storage, (BucketInfo.BuilderImpl) BucketInfo.newBuilder(bucketName));
+    }
+
+    public static Blob createBlob(final Storage storage, final String bucketName, final String blobName, final long blobSize) {
+        return new Blob(storage, (BlobInfo.BuilderImpl) BlobInfo.newBuilder(bucketName, blobName).setSize(blobSize));
+    }
+}

+ 1 - 1
plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java

@@ -19,7 +19,7 @@
 
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.services.storage.Storage;
+import com.google.cloud.storage.Storage;
 import org.elasticsearch.cluster.metadata.RepositoryMetaData;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.ByteSizeUnit;

+ 82 - 50
plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageClientSettingsTests.java

@@ -18,20 +18,25 @@
  */
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
 import com.google.api.services.storage.StorageScopes;
+import com.google.auth.oauth2.ServiceAccountCredentials;
+
 import org.elasticsearch.common.collect.Tuple;
 import org.elasticsearch.common.settings.MockSecureSettings;
+import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.test.ESTestCase;
 
+import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.util.ArrayList;
 import java.util.Base64;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
@@ -39,6 +44,7 @@ import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSetting
 import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.CONNECT_TIMEOUT_SETTING;
 import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.CREDENTIALS_FILE_SETTING;
 import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.ENDPOINT_SETTING;
+import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.PROJECT_ID_SETTING;
 import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.READ_TIMEOUT_SETTING;
 import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.getClientSettings;
 import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSettings.loadCredential;
@@ -46,59 +52,78 @@ import static org.elasticsearch.repositories.gcs.GoogleCloudStorageClientSetting
 public class GoogleCloudStorageClientSettingsTests extends ESTestCase {
 
     public void testLoadWithEmptySettings() {
-        Map<String, GoogleCloudStorageClientSettings> clientsSettings = GoogleCloudStorageClientSettings.load(Settings.EMPTY);
+        final Map<String, GoogleCloudStorageClientSettings> clientsSettings = GoogleCloudStorageClientSettings.load(Settings.EMPTY);
         assertEquals(1, clientsSettings.size());
         assertNotNull(clientsSettings.get("default"));
     }
 
     public void testLoad() throws Exception {
         final int nbClients = randomIntBetween(1, 5);
-        final Tuple<Map<String, GoogleCloudStorageClientSettings>, Settings> randomClients = randomClients(nbClients);
+        final List<Setting<?>> deprecationWarnings = new ArrayList<>();
+        final Tuple<Map<String, GoogleCloudStorageClientSettings>, Settings> randomClients = randomClients(nbClients, deprecationWarnings);
         final Map<String, GoogleCloudStorageClientSettings> expectedClientsSettings = randomClients.v1();
 
-        Map<String, GoogleCloudStorageClientSettings> actualClientsSettings = GoogleCloudStorageClientSettings.load(randomClients.v2());
+        final Map<String, GoogleCloudStorageClientSettings> actualClientsSettings = GoogleCloudStorageClientSettings
+                .load(randomClients.v2());
         assertEquals(expectedClientsSettings.size(), actualClientsSettings.size());
 
-        for (String clientName : expectedClientsSettings.keySet()) {
-            GoogleCloudStorageClientSettings actualClientSettings = actualClientsSettings.get(clientName);
+        for (final String clientName : expectedClientsSettings.keySet()) {
+            final GoogleCloudStorageClientSettings actualClientSettings = actualClientsSettings.get(clientName);
             assertNotNull(actualClientSettings);
-            GoogleCloudStorageClientSettings expectedClientSettings = expectedClientsSettings.get(clientName);
+            final GoogleCloudStorageClientSettings expectedClientSettings = expectedClientsSettings.get(clientName);
             assertNotNull(expectedClientSettings);
-
             assertGoogleCredential(expectedClientSettings.getCredential(), actualClientSettings.getCredential());
-            assertEquals(expectedClientSettings.getEndpoint(), actualClientSettings.getEndpoint());
+            assertEquals(expectedClientSettings.getHost(), actualClientSettings.getHost());
+            assertEquals(expectedClientSettings.getProjectId(), actualClientSettings.getProjectId());
             assertEquals(expectedClientSettings.getConnectTimeout(), actualClientSettings.getConnectTimeout());
             assertEquals(expectedClientSettings.getReadTimeout(), actualClientSettings.getReadTimeout());
             assertEquals(expectedClientSettings.getApplicationName(), actualClientSettings.getApplicationName());
         }
+
+        if (deprecationWarnings.isEmpty() == false) {
+            assertSettingDeprecationsAndWarnings(deprecationWarnings.toArray(new Setting<?>[0]));
+        }
     }
 
     public void testLoadCredential() throws Exception {
-        Tuple<Map<String, GoogleCloudStorageClientSettings>, Settings> randomClient = randomClients(1);
-        GoogleCloudStorageClientSettings expectedClientSettings = randomClient.v1().values().iterator().next();
-        String clientName = randomClient.v1().keySet().iterator().next();
-
+        final List<Setting<?>> deprecationWarnings = new ArrayList<>();
+        final Tuple<Map<String, GoogleCloudStorageClientSettings>, Settings> randomClient = randomClients(1, deprecationWarnings);
+        final GoogleCloudStorageClientSettings expectedClientSettings = randomClient.v1().values().iterator().next();
+        final String clientName = randomClient.v1().keySet().iterator().next();
         assertGoogleCredential(expectedClientSettings.getCredential(), loadCredential(randomClient.v2(), clientName));
     }
 
+    public void testProjectIdDefaultsToCredentials() throws Exception {
+        final String clientName = randomAlphaOfLength(5);
+        final Tuple<ServiceAccountCredentials, byte[]> credentials = randomCredential(clientName);
+        final ServiceAccountCredentials credential = credentials.v1();
+        final GoogleCloudStorageClientSettings googleCloudStorageClientSettings = new GoogleCloudStorageClientSettings(credential,
+                ENDPOINT_SETTING.getDefault(Settings.EMPTY), PROJECT_ID_SETTING.getDefault(Settings.EMPTY),
+                CONNECT_TIMEOUT_SETTING.getDefault(Settings.EMPTY), READ_TIMEOUT_SETTING.getDefault(Settings.EMPTY),
+                APPLICATION_NAME_SETTING.getDefault(Settings.EMPTY), new URI(""));
+        assertEquals(credential.getProjectId(), googleCloudStorageClientSettings.getProjectId());
+    }
+
     /** Generates a given number of GoogleCloudStorageClientSettings along with the Settings to build them from **/
-    private Tuple<Map<String, GoogleCloudStorageClientSettings>, Settings> randomClients(final int nbClients) throws Exception {
+    private Tuple<Map<String, GoogleCloudStorageClientSettings>, Settings> randomClients(final int nbClients,
+                                                                                         final List<Setting<?>> deprecationWarnings)
+            throws Exception {
         final Map<String, GoogleCloudStorageClientSettings> expectedClients = new HashMap<>();
-        expectedClients.put("default", getClientSettings(Settings.EMPTY, "default"));
 
         final Settings.Builder settings = Settings.builder();
         final MockSecureSettings secureSettings = new MockSecureSettings();
 
         for (int i = 0; i < nbClients; i++) {
-            String clientName = randomAlphaOfLength(5).toLowerCase(Locale.ROOT);
-
-            GoogleCloudStorageClientSettings clientSettings = randomClient(clientName, settings, secureSettings);
+            final String clientName = randomAlphaOfLength(5).toLowerCase(Locale.ROOT);
+            final GoogleCloudStorageClientSettings clientSettings = randomClient(clientName, settings, secureSettings, deprecationWarnings);
             expectedClients.put(clientName, clientSettings);
         }
 
         if (randomBoolean()) {
-            GoogleCloudStorageClientSettings clientSettings = randomClient("default", settings, secureSettings);
+            final GoogleCloudStorageClientSettings clientSettings = randomClient("default", settings, secureSettings, deprecationWarnings);
             expectedClients.put("default", clientSettings);
+        } else {
+            expectedClients.put("default", getClientSettings(Settings.EMPTY, "default"));
         }
 
         return Tuple.tuple(expectedClients, settings.setSecureSettings(secureSettings).build());
@@ -107,20 +132,30 @@ public class GoogleCloudStorageClientSettingsTests extends ESTestCase {
     /** Generates a random GoogleCloudStorageClientSettings along with the Settings to build it **/
     private static GoogleCloudStorageClientSettings randomClient(final String clientName,
                                                                  final Settings.Builder settings,
-                                                                 final MockSecureSettings secureSettings) throws Exception {
+                                                                 final MockSecureSettings secureSettings,
+                                                                 final List<Setting<?>> deprecationWarnings) throws Exception {
 
-        Tuple<GoogleCredential, byte[]> credentials = randomCredential(clientName);
-        GoogleCredential credential = credentials.v1();
+        final Tuple<ServiceAccountCredentials, byte[]> credentials = randomCredential(clientName);
+        final ServiceAccountCredentials credential = credentials.v1();
         secureSettings.setFile(CREDENTIALS_FILE_SETTING.getConcreteSettingForNamespace(clientName).getKey(), credentials.v2());
 
         String endpoint;
         if (randomBoolean()) {
-            endpoint = randomAlphaOfLength(5);
+            endpoint = randomFrom("http://www.elastic.co", "http://metadata.google.com:88/oauth", "https://www.googleapis.com",
+                    "https://www.elastic.co:443", "http://localhost:8443", "https://www.googleapis.com/oauth/token");
             settings.put(ENDPOINT_SETTING.getConcreteSettingForNamespace(clientName).getKey(), endpoint);
         } else {
             endpoint = ENDPOINT_SETTING.getDefault(Settings.EMPTY);
         }
 
+        String projectId;
+        if (randomBoolean()) {
+            projectId = randomAlphaOfLength(5);
+            settings.put(PROJECT_ID_SETTING.getConcreteSettingForNamespace(clientName).getKey(), projectId);
+        } else {
+            projectId = PROJECT_ID_SETTING.getDefault(Settings.EMPTY);
+        }
+
         TimeValue connectTimeout;
         if (randomBoolean()) {
             connectTimeout = randomTimeout();
@@ -141,40 +176,35 @@ public class GoogleCloudStorageClientSettingsTests extends ESTestCase {
         if (randomBoolean()) {
             applicationName = randomAlphaOfLength(5);
             settings.put(APPLICATION_NAME_SETTING.getConcreteSettingForNamespace(clientName).getKey(), applicationName);
+            deprecationWarnings.add(APPLICATION_NAME_SETTING.getConcreteSettingForNamespace(clientName));
         } else {
             applicationName = APPLICATION_NAME_SETTING.getDefault(Settings.EMPTY);
         }
 
-        return new GoogleCloudStorageClientSettings(credential, endpoint, connectTimeout, readTimeout, applicationName);
+        return new GoogleCloudStorageClientSettings(credential, endpoint, projectId, connectTimeout, readTimeout, applicationName,
+                new URI(""));
     }
 
     /** Generates a random GoogleCredential along with its corresponding Service Account file provided as a byte array **/
-    private static Tuple<GoogleCredential, byte[]> randomCredential(final String clientName) throws Exception {
-        KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
-
-        GoogleCredential.Builder credentialBuilder = new GoogleCredential.Builder();
-        credentialBuilder.setServiceAccountId(clientName);
-        credentialBuilder.setServiceAccountProjectId("project_id_" + clientName);
-        credentialBuilder.setServiceAccountScopes(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL));
-        credentialBuilder.setServiceAccountPrivateKey(keyPair.getPrivate());
-        credentialBuilder.setServiceAccountPrivateKeyId("private_key_id_" + clientName);
-
-        String encodedPrivateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
-        String serviceAccount = "{\"type\":\"service_account\"," +
+    private static Tuple<ServiceAccountCredentials, byte[]> randomCredential(final String clientName) throws Exception {
+        final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+        final ServiceAccountCredentials.Builder credentialBuilder = ServiceAccountCredentials.newBuilder();
+        credentialBuilder.setClientId("id_" + clientName);
+        credentialBuilder.setClientEmail(clientName);
+        credentialBuilder.setProjectId("project_id_" + clientName);
+        credentialBuilder.setPrivateKey(keyPair.getPrivate());
+        credentialBuilder.setPrivateKeyId("private_key_id_" + clientName);
+        credentialBuilder.setScopes(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL));
+        final String encodedPrivateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
+        final String serviceAccount = "{\"type\":\"service_account\"," +
             "\"project_id\":\"project_id_" + clientName + "\"," +
             "\"private_key_id\":\"private_key_id_" + clientName + "\"," +
             "\"private_key\":\"-----BEGIN PRIVATE KEY-----\\n" +
             encodedPrivateKey +
             "\\n-----END PRIVATE KEY-----\\n\"," +
             "\"client_email\":\"" + clientName + "\"," +
-            "\"client_id\":\"id_" + clientName + "\"," +
-            "\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\"," +
-            "\"token_uri\":\"https://accounts.google.com/o/oauth2/token\"," +
-            "\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"," +
-            "\"client_x509_cert_url\":\"https://www.googleapis.com/robot/v1/metadata/x509/" +
-            clientName +
-            "%40appspot.gserviceaccount.com\"}";
-
+            "\"client_id\":\"id_" + clientName + "\"" +
+            "}";
         return Tuple.tuple(credentialBuilder.build(), serviceAccount.getBytes(StandardCharsets.UTF_8));
     }
 
@@ -182,14 +212,16 @@ public class GoogleCloudStorageClientSettingsTests extends ESTestCase {
         return randomFrom(TimeValue.MINUS_ONE, TimeValue.ZERO, TimeValue.parseTimeValue(randomPositiveTimeValue(), "test"));
     }
 
-    private static void assertGoogleCredential(final GoogleCredential expected, final GoogleCredential actual) {
+    private static void assertGoogleCredential(ServiceAccountCredentials expected, ServiceAccountCredentials actual) {
         if (expected != null) {
             assertEquals(expected.getServiceAccountUser(), actual.getServiceAccountUser());
-            assertEquals(expected.getServiceAccountId(), actual.getServiceAccountId());
-            assertEquals(expected.getServiceAccountProjectId(), actual.getServiceAccountProjectId());
-            assertEquals(expected.getServiceAccountScopesAsString(), actual.getServiceAccountScopesAsString());
-            assertEquals(expected.getServiceAccountPrivateKey(), actual.getServiceAccountPrivateKey());
-            assertEquals(expected.getServiceAccountPrivateKeyId(), actual.getServiceAccountPrivateKeyId());
+            assertEquals(expected.getClientId(), actual.getClientId());
+            assertEquals(expected.getClientEmail(), actual.getClientEmail());
+            assertEquals(expected.getAccount(), actual.getAccount());
+            assertEquals(expected.getProjectId(), actual.getProjectId());
+            assertEquals(expected.getScopes(), actual.getScopes());
+            assertEquals(expected.getPrivateKey(), actual.getPrivateKey());
+            assertEquals(expected.getPrivateKeyId(), actual.getPrivateKeyId());
         } else {
             assertNull(actual);
         }

+ 45 - 59
plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageServiceTests.java

@@ -19,79 +19,65 @@
 
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
-import com.google.api.client.http.GenericUrl;
-import com.google.api.client.http.HttpIOExceptionHandler;
-import com.google.api.client.http.HttpRequest;
-import com.google.api.client.http.HttpRequestFactory;
-import com.google.api.client.http.HttpRequestInitializer;
-import com.google.api.client.http.HttpResponse;
-import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
-import com.google.api.client.testing.http.MockHttpTransport;
+import com.google.auth.Credentials;
+import com.google.cloud.http.HttpTransportOptions;
+import com.google.cloud.storage.Storage;
+
+import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.env.Environment;
 import org.elasticsearch.test.ESTestCase;
+import org.hamcrest.Matchers;
+import java.util.Collections;
+import java.util.Locale;
 
-import java.io.IOException;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class GoogleCloudStorageServiceTests extends ESTestCase {
 
-    /**
-     * Test that the {@link GoogleCloudStorageService.DefaultHttpRequestInitializer} attaches new instances
-     * of {@link HttpIOExceptionHandler} and {@link HttpUnsuccessfulResponseHandler} for every HTTP requests.
-     */
-    public void testDefaultHttpRequestInitializer() throws IOException {
+    public void testClientInitializer() throws Exception {
+        final String clientName = randomAlphaOfLength(4).toLowerCase(Locale.ROOT);
         final Environment environment = mock(Environment.class);
-        when(environment.settings()).thenReturn(Settings.EMPTY);
-
-        final GoogleCredential credential = mock(GoogleCredential.class);
-        when(credential.handleResponse(any(HttpRequest.class), any(HttpResponse.class), anyBoolean())).thenReturn(false);
-
-        final TimeValue readTimeout = TimeValue.timeValueSeconds(randomIntBetween(1, 120));
-        final TimeValue connectTimeout = TimeValue.timeValueSeconds(randomIntBetween(1, 120));
-        final String endpoint = randomBoolean() ? randomAlphaOfLength(10) : null;
-        final String applicationName = randomBoolean() ? randomAlphaOfLength(10) : null;
-
-        final GoogleCloudStorageClientSettings clientSettings =
-            new GoogleCloudStorageClientSettings(credential, endpoint, connectTimeout, readTimeout, applicationName);
-
-        final HttpRequestInitializer initializer = GoogleCloudStorageService.createRequestInitializer(clientSettings);
-        final HttpRequestFactory requestFactory = new MockHttpTransport().createRequestFactory(initializer);
-
-        final HttpRequest request1 = requestFactory.buildGetRequest(new GenericUrl());
-        assertEquals((int) connectTimeout.millis(), request1.getConnectTimeout());
-        assertEquals((int) readTimeout.millis(), request1.getReadTimeout());
-        assertSame(credential, request1.getInterceptor());
-        assertNotNull(request1.getIOExceptionHandler());
-        assertNotNull(request1.getUnsuccessfulResponseHandler());
-
-        final HttpRequest request2 = requestFactory.buildGetRequest(new GenericUrl());
-        assertEquals((int) connectTimeout.millis(), request2.getConnectTimeout());
-        assertEquals((int) readTimeout.millis(), request2.getReadTimeout());
-        assertSame(request1.getInterceptor(), request2.getInterceptor());
-        assertNotNull(request2.getIOExceptionHandler());
-        assertNotSame(request1.getIOExceptionHandler(), request2.getIOExceptionHandler());
-        assertNotNull(request2.getUnsuccessfulResponseHandler());
-        assertNotSame(request1.getUnsuccessfulResponseHandler(), request2.getUnsuccessfulResponseHandler());
-
-        request1.getUnsuccessfulResponseHandler().handleResponse(null, null, false);
-        verify(credential, times(1)).handleResponse(any(HttpRequest.class), any(HttpResponse.class), anyBoolean());
-
-        request2.getUnsuccessfulResponseHandler().handleResponse(null, null, false);
-        verify(credential, times(2)).handleResponse(any(HttpRequest.class), any(HttpResponse.class), anyBoolean());
+        final TimeValue connectTimeValue = TimeValue.timeValueNanos(randomIntBetween(0, 2000000));
+        final TimeValue readTimeValue = TimeValue.timeValueNanos(randomIntBetween(0, 2000000));
+        final String applicationName = randomAlphaOfLength(4);
+        final String hostName = randomFrom("http://", "https://") + randomAlphaOfLength(4) + ":" + randomIntBetween(1, 65535);
+        final String projectIdName = randomAlphaOfLength(4);
+        final Settings settings = Settings.builder()
+                .put(GoogleCloudStorageClientSettings.CONNECT_TIMEOUT_SETTING.getConcreteSettingForNamespace(clientName).getKey(),
+                        connectTimeValue.getStringRep())
+                .put(GoogleCloudStorageClientSettings.READ_TIMEOUT_SETTING.getConcreteSettingForNamespace(clientName).getKey(),
+                        readTimeValue.getStringRep())
+                .put(GoogleCloudStorageClientSettings.APPLICATION_NAME_SETTING.getConcreteSettingForNamespace(clientName).getKey(),
+                        applicationName)
+                .put(GoogleCloudStorageClientSettings.ENDPOINT_SETTING.getConcreteSettingForNamespace(clientName).getKey(), hostName)
+                .put(GoogleCloudStorageClientSettings.PROJECT_ID_SETTING.getConcreteSettingForNamespace(clientName).getKey(), projectIdName)
+                .build();
+        when(environment.settings()).thenReturn(settings);
+        final GoogleCloudStorageClientSettings clientSettings = GoogleCloudStorageClientSettings.getClientSettings(settings, clientName);
+        final GoogleCloudStorageService service = new GoogleCloudStorageService(environment,
+                Collections.singletonMap(clientName, clientSettings));
+        final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> service.createClient("another_client"));
+        assertThat(e.getMessage(), Matchers.startsWith("Unknown client name"));
+        assertSettingDeprecationsAndWarnings(
+                new Setting<?>[] { GoogleCloudStorageClientSettings.APPLICATION_NAME_SETTING.getConcreteSettingForNamespace(clientName) });
+        final Storage storage = service.createClient(clientName);
+        assertThat(storage.getOptions().getApplicationName(), Matchers.containsString(applicationName));
+        assertThat(storage.getOptions().getHost(), Matchers.is(hostName));
+        assertThat(storage.getOptions().getProjectId(), Matchers.is(projectIdName));
+        assertThat(storage.getOptions().getTransportOptions(), Matchers.instanceOf(HttpTransportOptions.class));
+        assertThat(((HttpTransportOptions) storage.getOptions().getTransportOptions()).getConnectTimeout(),
+                Matchers.is((int) connectTimeValue.millis()));
+        assertThat(((HttpTransportOptions) storage.getOptions().getTransportOptions()).getReadTimeout(),
+                Matchers.is((int) readTimeValue.millis()));
+        assertThat(storage.getOptions().getCredentials(), Matchers.nullValue(Credentials.class));
     }
 
     public void testToTimeout() {
-        assertNull(GoogleCloudStorageService.toTimeout(null));
-        assertNull(GoogleCloudStorageService.toTimeout(TimeValue.ZERO));
+        assertEquals(-1, GoogleCloudStorageService.toTimeout(null).intValue());
+        assertEquals(-1, GoogleCloudStorageService.toTimeout(TimeValue.ZERO).intValue());
         assertEquals(0, GoogleCloudStorageService.toTimeout(TimeValue.MINUS_ONE).intValue());
     }
 }

+ 408 - 219
plugins/repository-gcs/src/test/java/org/elasticsearch/repositories/gcs/MockStorage.java

@@ -19,289 +19,478 @@
 
 package org.elasticsearch.repositories.gcs;
 
-import com.google.api.client.googleapis.json.GoogleJsonError;
-import com.google.api.client.googleapis.json.GoogleJsonResponseException;
-import com.google.api.client.http.AbstractInputStreamContent;
-import com.google.api.client.http.HttpHeaders;
-import com.google.api.client.http.HttpMethods;
-import com.google.api.client.http.HttpRequest;
-import com.google.api.client.http.HttpRequestInitializer;
-import com.google.api.client.http.HttpResponseException;
-import com.google.api.client.http.LowLevelHttpRequest;
-import com.google.api.client.http.LowLevelHttpResponse;
-import com.google.api.client.http.MultipartContent;
-import com.google.api.client.json.JsonFactory;
-import com.google.api.client.testing.http.MockHttpTransport;
-import com.google.api.client.testing.http.MockLowLevelHttpRequest;
-import com.google.api.client.testing.http.MockLowLevelHttpResponse;
-import com.google.api.services.storage.Storage;
-import com.google.api.services.storage.model.Bucket;
-import com.google.api.services.storage.model.StorageObject;
-import org.elasticsearch.common.io.Streams;
-import org.elasticsearch.rest.RestStatus;
+import com.google.api.gax.paging.Page;
+import com.google.cloud.Policy;
+import com.google.cloud.ReadChannel;
+import com.google.cloud.RestorableState;
+import com.google.cloud.WriteChannel;
+import com.google.cloud.storage.Acl;
+import com.google.cloud.storage.Blob;
+import com.google.cloud.storage.BlobId;
+import com.google.cloud.storage.BlobInfo;
+import com.google.cloud.storage.Bucket;
+import com.google.cloud.storage.BucketInfo;
+import com.google.cloud.storage.CopyWriter;
+import com.google.cloud.storage.ServiceAccount;
+import com.google.cloud.storage.Storage;
+import com.google.cloud.storage.StorageBatch;
+import com.google.cloud.storage.StorageException;
+import com.google.cloud.storage.StorageOptions;
+import com.google.cloud.storage.StorageRpcOptionUtils;
+import com.google.cloud.storage.StorageTestUtils;
+
+import org.elasticsearch.core.internal.io.IOUtils;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigInteger;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentMap;
-
-import static org.mockito.Mockito.mock;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * {@link MockStorage} mocks a {@link Storage} client by storing all the blobs
  * in a given concurrent map.
  */
-class MockStorage extends Storage {
-
-    /* A custom HTTP header name used to propagate the name of the blobs to delete in batch requests */
-    private static final String DELETION_HEADER = "x-blob-to-delete";
+class MockStorage implements Storage {
 
     private final String bucketName;
     private final ConcurrentMap<String, byte[]> blobs;
 
     MockStorage(final String bucket, final ConcurrentMap<String, byte[]> blobs) {
-        super(new MockedHttpTransport(blobs), mock(JsonFactory.class), mock(HttpRequestInitializer.class));
-        this.bucketName = bucket;
-        this.blobs = blobs;
+        this.bucketName = Objects.requireNonNull(bucket);
+        this.blobs = Objects.requireNonNull(blobs);
     }
 
     @Override
-    public Buckets buckets() {
-        return new MockBuckets();
+    public Bucket get(String bucket, BucketGetOption... options) {
+        if (bucketName.equals(bucket)) {
+            return StorageTestUtils.createBucket(this, bucketName);
+        } else {
+            return null;
+        }
     }
 
     @Override
-    public Objects objects() {
-        return new MockObjects();
+    public Blob get(BlobId blob) {
+        if (bucketName.equals(blob.getBucket())) {
+            final byte[] bytes = blobs.get(blob.getName());
+            if (bytes != null) {
+                return StorageTestUtils.createBlob(this, bucketName, blob.getName(), bytes.length);
+            }
+        }
+        return null;
     }
 
-    class MockBuckets extends Buckets {
+    @Override
+    public boolean delete(BlobId blob) {
+        if (bucketName.equals(blob.getBucket()) && blobs.containsKey(blob.getName())) {
+            return blobs.remove(blob.getName()) != null;
+        }
+        return false;
+    }
 
-        @Override
-        public Get get(String getBucket) {
-            return new Get(getBucket) {
-                @Override
-                public Bucket execute() {
-                    if (bucketName.equals(getBucket())) {
-                        Bucket bucket = new Bucket();
-                        bucket.setId(bucketName);
-                        return bucket;
-                    } else {
-                        return null;
-                    }
-                }
-            };
+    @Override
+    public List<Boolean> delete(Iterable<BlobId> blobIds) {
+        final List<Boolean> ans = new ArrayList<>();
+        for (final BlobId blobId : blobIds) {
+            ans.add(delete(blobId));
         }
+        return ans;
     }
 
-    class MockObjects extends Objects {
+    @Override
+    public Blob create(BlobInfo blobInfo, byte[] content, BlobTargetOption... options) {
+        if (bucketName.equals(blobInfo.getBucket()) == false) {
+            throw new StorageException(404, "Bucket not found");
+        }
+        blobs.put(blobInfo.getName(), content);
+        return get(BlobId.of(blobInfo.getBucket(), blobInfo.getName()));
+    }
+
+    @Override
+    public CopyWriter copy(CopyRequest copyRequest) {
+        if (bucketName.equals(copyRequest.getSource().getBucket()) == false) {
+            throw new StorageException(404, "Source bucket not found");
+        }
+        if (bucketName.equals(copyRequest.getTarget().getBucket()) == false) {
+            throw new StorageException(404, "Target bucket not found");
+        }
+
+        final byte[] bytes = blobs.get(copyRequest.getSource().getName());
+        if (bytes == null) {
+            throw new StorageException(404, "Source blob does not exist");
+        }
+        blobs.put(copyRequest.getTarget().getName(), bytes);
+        return StorageRpcOptionUtils
+                .createCopyWriter(get(BlobId.of(copyRequest.getTarget().getBucket(), copyRequest.getTarget().getName())));
+    }
+
+    @Override
+    public Page<Blob> list(String bucket, BlobListOption... options) {
+        if (bucketName.equals(bucket) == false) {
+            throw new StorageException(404, "Bucket not found");
+        }
+        final Storage storage = this;
+        final String prefix = StorageRpcOptionUtils.getPrefix(options);
 
-        @Override
-        public Get get(String getBucket, String getObject) {
-            return new Get(getBucket, getObject) {
+        return new Page<Blob>() {
+            @Override
+            public boolean hasNextPage() {
+                return false;
+            }
+
+            @Override
+            public String getNextPageToken() {
+                return null;
+            }
+
+            @Override
+            public Page<Blob> getNextPage() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public Iterable<Blob> iterateAll() {
+                return blobs.entrySet().stream()
+                    .filter(blob -> ((prefix == null) || blob.getKey().startsWith(prefix)))
+                    .map(blob -> StorageTestUtils.createBlob(storage, bucketName, blob.getKey(), blob.getValue().length))
+                    .collect(Collectors.toList());
+            }
+
+            @Override
+            public Iterable<Blob> getValues() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    @Override
+    public ReadChannel reader(BlobId blob, BlobSourceOption... options) {
+        if (bucketName.equals(blob.getBucket())) {
+            final byte[] bytes = blobs.get(blob.getName());
+            final ReadableByteChannel readableByteChannel = Channels.newChannel(new ByteArrayInputStream(bytes));
+            return new ReadChannel() {
                 @Override
-                public StorageObject execute() throws IOException {
-                    if (bucketName.equals(getBucket()) == false) {
-                        throw newBucketNotFoundException(getBucket());
-                    }
-                    if (blobs.containsKey(getObject()) == false) {
-                        throw newObjectNotFoundException(getObject());
-                    }
-
-                    StorageObject storageObject = new StorageObject();
-                    storageObject.setId(getObject());
-                    return storageObject;
+                public void close() {
+                    IOUtils.closeWhileHandlingException(readableByteChannel);
                 }
 
                 @Override
-                public InputStream executeMediaAsInputStream() throws IOException {
-                    if (bucketName.equals(getBucket()) == false) {
-                        throw newBucketNotFoundException(getBucket());
-                    }
-                    if (blobs.containsKey(getObject()) == false) {
-                        throw newObjectNotFoundException(getObject());
-                    }
-                    return new ByteArrayInputStream(blobs.get(getObject()));
+                public void seek(long position) throws IOException {
+                    throw new UnsupportedOperationException();
                 }
-            };
-        }
 
-        @Override
-        public Insert insert(String insertBucket, StorageObject insertObject, AbstractInputStreamContent insertStream) {
-            return new Insert(insertBucket, insertObject) {
                 @Override
-                public StorageObject execute() throws IOException {
-                    if (bucketName.equals(getBucket()) == false) {
-                        throw newBucketNotFoundException(getBucket());
-                    }
-
-                    ByteArrayOutputStream out = new ByteArrayOutputStream();
-                    Streams.copy(insertStream.getInputStream(), out);
-                    blobs.put(getName(), out.toByteArray());
-                    return null;
+                public void setChunkSize(int chunkSize) {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public RestorableState<ReadChannel> capture() {
+                    throw new UnsupportedOperationException();
+                }
+
+                @Override
+                public int read(ByteBuffer dst) throws IOException {
+                    return readableByteChannel.read(dst);
                 }
-            };
-        }
 
-        @Override
-        public List list(String listBucket) {
-            return new List(listBucket) {
                 @Override
-                public com.google.api.services.storage.model.Objects execute() throws IOException {
-                    if (bucketName.equals(getBucket()) == false) {
-                        throw newBucketNotFoundException(getBucket());
-                    }
-
-                    final com.google.api.services.storage.model.Objects objects = new com.google.api.services.storage.model.Objects();
-
-                    final java.util.List<StorageObject> storageObjects = new ArrayList<>();
-                    for (Entry<String, byte[]> blob : blobs.entrySet()) {
-                        if (getPrefix() == null || blob.getKey().startsWith(getPrefix())) {
-                            StorageObject storageObject = new StorageObject();
-                            storageObject.setId(blob.getKey());
-                            storageObject.setName(blob.getKey());
-                            storageObject.setSize(BigInteger.valueOf((long) blob.getValue().length));
-                            storageObjects.add(storageObject);
-                        }
-                    }
-
-                    objects.setItems(storageObjects);
-                    return objects;
+                public boolean isOpen() {
+                    return readableByteChannel.isOpen();
                 }
             };
         }
+        return null;
+    }
+
+    @Override
+    public WriteChannel writer(BlobInfo blobInfo, BlobWriteOption... options) {
+        if (bucketName.equals(blobInfo.getBucket())) {
+            final ByteArrayOutputStream output = new ByteArrayOutputStream();
+            return new WriteChannel() {
+
+                final WritableByteChannel writableByteChannel = Channels.newChannel(output);
 
-        @Override
-        public Delete delete(String deleteBucket, String deleteObject) {
-            return new Delete(deleteBucket, deleteObject) {
                 @Override
-                public Void execute() throws IOException {
-                    if (bucketName.equals(getBucket()) == false) {
-                        throw newBucketNotFoundException(getBucket());
-                    }
+                public void setChunkSize(int chunkSize) {
+                    throw new UnsupportedOperationException();
+                }
 
-                    if (blobs.containsKey(getObject()) == false) {
-                        throw newObjectNotFoundException(getObject());
-                    }
+                @Override
+                public RestorableState<WriteChannel> capture() {
+                    throw new UnsupportedOperationException();
+                }
 
-                    blobs.remove(getObject());
-                    return null;
+                @Override
+                public int write(ByteBuffer src) throws IOException {
+                    return writableByteChannel.write(src);
                 }
 
                 @Override
-                public HttpRequest buildHttpRequest() throws IOException {
-                    HttpRequest httpRequest = super.buildHttpRequest();
-                    httpRequest.getHeaders().put(DELETION_HEADER, getObject());
-                    return httpRequest;
+                public boolean isOpen() {
+                    return writableByteChannel.isOpen();
                 }
-            };
-        }
 
-        @Override
-        public Copy copy(String srcBucket, String srcObject, String destBucket, String destObject, StorageObject content) {
-            return new Copy(srcBucket, srcObject, destBucket, destObject, content) {
                 @Override
-                public StorageObject execute() throws IOException {
-                    if (bucketName.equals(getSourceBucket()) == false) {
-                        throw newBucketNotFoundException(getSourceBucket());
-                    }
-                    if (bucketName.equals(getDestinationBucket()) == false) {
-                        throw newBucketNotFoundException(getDestinationBucket());
-                    }
-
-                    final byte[] bytes = blobs.get(getSourceObject());
-                    if (bytes == null) {
-                        throw newObjectNotFoundException(getSourceObject());
-                    }
-                    blobs.put(getDestinationObject(), bytes);
-
-                    StorageObject storageObject = new StorageObject();
-                    storageObject.setId(getDestinationObject());
-                    return storageObject;
+                public void close() throws IOException {
+                    IOUtils.closeWhileHandlingException(writableByteChannel);
+                    blobs.put(blobInfo.getName(), output.toByteArray());
                 }
             };
         }
+        return null;
     }
 
-    private static GoogleJsonResponseException newBucketNotFoundException(final String bucket) {
-        HttpResponseException.Builder builder = new HttpResponseException.Builder(404, "Bucket not found: " + bucket, new HttpHeaders());
-        return new GoogleJsonResponseException(builder, new GoogleJsonError());
+    // Everything below this line is not implemented.
+
+    @Override
+    public Bucket create(BucketInfo bucketInfo, BucketTargetOption... options) {
+        return null;
     }
 
-    private static GoogleJsonResponseException newObjectNotFoundException(final String object) {
-        HttpResponseException.Builder builder = new HttpResponseException.Builder(404, "Object not found: " + object, new HttpHeaders());
-        return new GoogleJsonResponseException(builder, new GoogleJsonError());
+    @Override
+    public Blob create(BlobInfo blobInfo, BlobTargetOption... options) {
+        return null;
     }
 
-    /**
-     * {@link MockedHttpTransport} extends the existing testing transport to analyze the content
-     * of {@link com.google.api.client.googleapis.batch.BatchRequest} and delete the appropriates
-     * blobs. We use this because {@link Storage#batch()} is final and there is no other way to
-     * extend batch requests for testing purposes.
-     */
-    static class MockedHttpTransport extends MockHttpTransport {
+    @Override
+    public Blob create(BlobInfo blobInfo, InputStream content, BlobWriteOption... options) {
+        return null;
+    }
 
-        private final ConcurrentMap<String, byte[]> blobs;
+    @Override
+    public Blob get(String bucket, String blob, BlobGetOption... options) {
+        return null;
+    }
 
-        MockedHttpTransport(final ConcurrentMap<String, byte[]> blobs) {
-            this.blobs = blobs;
-        }
+    @Override
+    public Blob get(BlobId blob, BlobGetOption... options) {
+        return null;
+    }
 
-        @Override
-        public LowLevelHttpRequest buildRequest(final String method, final String url) throws IOException {
-            // We analyze the content of the Batch request to detect our custom HTTP header,
-            // and extract from it the name of the blob to delete. Then we reply a simple
-            // batch response so that the client parser is happy.
-            //
-            // See https://cloud.google.com/storage/docs/json_api/v1/how-tos/batch for the
-            // format of the batch request body.
-            if (HttpMethods.POST.equals(method) && url.endsWith("/batch")) {
-                return new MockLowLevelHttpRequest() {
-                    @Override
-                    public LowLevelHttpResponse execute() throws IOException {
-                        final String contentType = new MultipartContent().getType();
-
-                        final StringBuilder builder = new StringBuilder();
-                        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-                            getStreamingContent().writeTo(out);
-
-                            Streams.readAllLines(new ByteArrayInputStream(out.toByteArray()), line -> {
-                                if (line != null && line.startsWith(DELETION_HEADER)) {
-                                    builder.append("--__END_OF_PART__\r\n");
-                                    builder.append("Content-Type: application/http").append("\r\n");
-                                    builder.append("\r\n");
-                                    builder.append("HTTP/1.1 ");
-
-                                    final String blobName = line.substring(line.indexOf(':') + 1).trim();
-                                    if (blobs.containsKey(blobName)) {
-                                        builder.append(RestStatus.OK.getStatus());
-                                        blobs.remove(blobName);
-                                    } else {
-                                        builder.append(RestStatus.NOT_FOUND.getStatus());
-                                    }
-                                    builder.append("\r\n");
-                                    builder.append("Content-Type: application/json; charset=UTF-8").append("\r\n");
-                                    builder.append("Content-Length: 0").append("\r\n");
-                                    builder.append("\r\n");
-                                }
-                            });
-                            builder.append("\r\n");
-                            builder.append("--__END_OF_PART__--");
-                        }
-
-                        MockLowLevelHttpResponse response = new MockLowLevelHttpResponse();
-                        response.setStatusCode(200);
-                        response.setContent(builder.toString());
-                        response.setContentType(contentType);
-                        return response;
-                    }
-                };
-            } else {
-                return super.buildRequest(method, url);
-            }
-        }
+    @Override
+    public Page<Bucket> list(BucketListOption... options) {
+        return null;
+    }
+
+    @Override
+    public Bucket update(BucketInfo bucketInfo, BucketTargetOption... options) {
+        return null;
+    }
+
+    @Override
+    public Blob update(BlobInfo blobInfo, BlobTargetOption... options) {
+        return null;
+    }
+
+    @Override
+    public Blob update(BlobInfo blobInfo) {
+        return null;
+    }
+
+    @Override
+    public boolean delete(String bucket, BucketSourceOption... options) {
+        return false;
+    }
+
+    @Override
+    public boolean delete(String bucket, String blob, BlobSourceOption... options) {
+        return false;
+    }
+
+    @Override
+    public boolean delete(BlobId blob, BlobSourceOption... options) {
+        return false;
+    }
+
+    @Override
+    public Blob compose(ComposeRequest composeRequest) {
+        return null;
+    }
+
+    @Override
+    public byte[] readAllBytes(String bucket, String blob, BlobSourceOption... options) {
+        return new byte[0];
+    }
+
+    @Override
+    public byte[] readAllBytes(BlobId blob, BlobSourceOption... options) {
+        return new byte[0];
+    }
+
+    @Override
+    public StorageBatch batch() {
+        return null;
+    }
+
+    @Override
+    public ReadChannel reader(String bucket, String blob, BlobSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public URL signUrl(BlobInfo blobInfo, long duration, TimeUnit unit, SignUrlOption... options) {
+        return null;
+    }
+
+    @Override
+    public List<Blob> get(BlobId... blobIds) {
+        return null;
+    }
+
+    @Override
+    public List<Blob> get(Iterable<BlobId> blobIds) {
+        return null;
+    }
+
+    @Override
+    public List<Blob> update(BlobInfo... blobInfos) {
+        return null;
+    }
+
+    @Override
+    public List<Blob> update(Iterable<BlobInfo> blobInfos) {
+        return null;
+    }
+
+    @Override
+    public List<Boolean> delete(BlobId... blobIds) {
+        return null;
+    }
+
+    @Override
+    public Acl getAcl(String bucket, Acl.Entity entity, BucketSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public Acl getAcl(String bucket, Acl.Entity entity) {
+        return null;
+    }
+
+    @Override
+    public boolean deleteAcl(String bucket, Acl.Entity entity, BucketSourceOption... options) {
+        return false;
+    }
+
+    @Override
+    public boolean deleteAcl(String bucket, Acl.Entity entity) {
+        return false;
+    }
+
+    @Override
+    public Acl createAcl(String bucket, Acl acl, BucketSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public Acl createAcl(String bucket, Acl acl) {
+        return null;
+    }
+
+    @Override
+    public Acl updateAcl(String bucket, Acl acl, BucketSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public Acl updateAcl(String bucket, Acl acl) {
+        return null;
+    }
+
+    @Override
+    public List<Acl> listAcls(String bucket, BucketSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public List<Acl> listAcls(String bucket) {
+        return null;
+    }
+
+    @Override
+    public Acl getDefaultAcl(String bucket, Acl.Entity entity) {
+        return null;
+    }
+
+    @Override
+    public boolean deleteDefaultAcl(String bucket, Acl.Entity entity) {
+        return false;
+    }
+
+    @Override
+    public Acl createDefaultAcl(String bucket, Acl acl) {
+        return null;
+    }
+
+    @Override
+    public Acl updateDefaultAcl(String bucket, Acl acl) {
+        return null;
+    }
+
+    @Override
+    public List<Acl> listDefaultAcls(String bucket) {
+        return null;
+    }
+
+    @Override
+    public Acl getAcl(BlobId blob, Acl.Entity entity) {
+        return null;
+    }
+
+    @Override
+    public boolean deleteAcl(BlobId blob, Acl.Entity entity) {
+        return false;
+    }
+
+    @Override
+    public Acl createAcl(BlobId blob, Acl acl) {
+        return null;
+    }
+
+    @Override
+    public Acl updateAcl(BlobId blob, Acl acl) {
+        return null;
+    }
+
+    @Override
+    public List<Acl> listAcls(BlobId blob) {
+        return null;
+    }
+
+    @Override
+    public Policy getIamPolicy(String bucket, BucketSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public Policy setIamPolicy(String bucket, Policy policy, BucketSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public List<Boolean> testIamPermissions(String bucket, List<String> permissions, BucketSourceOption... options) {
+        return null;
+    }
+
+    @Override
+    public ServiceAccount getServiceAccount(String projectId) {
+        return null;
+    }
+
+    @Override
+    public StorageOptions getOptions() {
+        return null;
     }
 }