You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ga...@apache.org on 2016/05/28 20:27:51 UTC
jclouds-labs git commit: JCLOUDS-1005: Backblaze B2 skeleton and
bucket ops
Repository: jclouds-labs
Updated Branches:
refs/heads/master 6d078e690 -> afca4993f
JCLOUDS-1005: Backblaze B2 skeleton and bucket ops
Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/afca4993
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/afca4993
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/afca4993
Branch: refs/heads/master
Commit: afca4993f80f74e13afb36fafcb5ac3837418376
Parents: 6d078e6
Author: Andrew Gaul <ga...@apache.org>
Authored: Thu May 19 10:50:45 2016 -0700
Committer: Andrew Gaul <ga...@apache.org>
Committed: Sat May 28 13:08:23 2016 -0700
----------------------------------------------------------------------
b2/pom.xml | 147 +++++++++++
b2/src/main/java/org/jclouds/b2/B2Api.java | 32 +++
.../main/java/org/jclouds/b2/B2ApiMetadata.java | 85 +++++++
.../java/org/jclouds/b2/B2ProviderMetadata.java | 66 +++++
.../org/jclouds/b2/B2ResponseException.java | 38 +++
.../org/jclouds/b2/config/B2HttpApiModule.java | 81 ++++++
.../org/jclouds/b2/domain/Authorization.java | 35 +++
.../java/org/jclouds/b2/domain/B2Error.java | 33 +++
.../main/java/org/jclouds/b2/domain/Bucket.java | 34 +++
.../java/org/jclouds/b2/domain/BucketList.java | 34 +++
.../java/org/jclouds/b2/domain/BucketType.java | 34 +++
.../jclouds/b2/features/AuthorizationApi.java | 37 +++
.../java/org/jclouds/b2/features/BucketApi.java | 75 ++++++
.../b2/filters/RequestAuthorization.java | 64 +++++
.../handlers/ParseB2ErrorFromJsonContent.java | 53 ++++
.../org/jclouds/b2/B2ProviderMetadataTest.java | 27 ++
.../jclouds/b2/features/BucketApiLiveTest.java | 106 ++++++++
.../jclouds/b2/features/BucketApiMockTest.java | 251 +++++++++++++++++++
.../jclouds/b2/internal/BaseB2ApiLiveTest.java | 33 +++
.../resources/authorize_account_response.json | 7 +
b2/src/test/resources/bucket.json | 6 +
.../test/resources/create_bucket_request.json | 5 +
.../delete_bucket_already_deleted_response.json | 5 +
.../test/resources/delete_bucket_request.json | 4 +
b2/src/test/resources/list_buckets_request.json | 3 +
.../test/resources/list_buckets_response.json | 21 ++
b2/src/test/resources/log4j.xml | 106 ++++++++
.../test/resources/update_bucket_request.json | 5 +
pom.xml | 1 +
29 files changed, 1428 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/pom.xml
----------------------------------------------------------------------
diff --git a/b2/pom.xml b/b2/pom.xml
new file mode 100644
index 0000000..6283223
--- /dev/null
+++ b/b2/pom.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF 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.
+
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.jclouds.labs</groupId>
+ <artifactId>jclouds-labs</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+
+ <!-- TODO: when out of labs, switch to org.jclouds.api -->
+ <groupId>org.apache.jclouds.labs</groupId>
+ <artifactId>b2</artifactId>
+ <name>Apache jclouds B2 API</name>
+ <description>BlobStore binding to the Backblaze B2 API</description>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <test.b2.identity>FIXME_IDENTITY</test.b2.identity>
+ <test.b2.credential>FIXME_CREDENTIAL</test.b2.credential>
+ <test.b2.build-version />
+ <jclouds.osgi.export>org.jclouds.b2*;version="${project.version}"</jclouds.osgi.export>
+ <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
+ </properties>
+
+ <!-- For modernizer, which depends on jclouds-resources snapshot. -->
+ <pluginRepositories>
+ <pluginRepository>
+ <id>apache-snapshots</id>
+ <url>https://repository.apache.org/content/repositories/snapshots</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.jclouds</groupId>
+ <artifactId>jclouds-blobstore</artifactId>
+ <version>${project.parent.version}</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jclouds</groupId>
+ <artifactId>jclouds-core</artifactId>
+ <version>${project.parent.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jclouds</groupId>
+ <artifactId>jclouds-blobstore</artifactId>
+ <version>${project.parent.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jclouds.driver</groupId>
+ <artifactId>jclouds-log4j</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.auto.value</groupId>
+ <artifactId>auto-value</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp</groupId>
+ <artifactId>mockwebserver</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.auto.service</groupId>
+ <artifactId>auto-service</artifactId>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>live</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>integration</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <groups>live</groups>
+ <excludedGroups>livelong</excludedGroups>
+ <systemPropertyVariables>
+ <jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
+ <jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
+ <test.b2.endpoint>${test.b2.endpoint}</test.b2.endpoint>
+ <test.b2.api-version>${test.b2.api-version}</test.b2.api-version>
+ <test.b2.build-version>${test.b2.build-version}</test.b2.build-version>
+ <test.b2.identity>${test.b2.identity}</test.b2.identity>
+ <test.b2.credential>${test.b2.credential}</test.b2.credential>
+ </systemPropertyVariables>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/B2Api.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/B2Api.java b/b2/src/main/java/org/jclouds/b2/B2Api.java
new file mode 100644
index 0000000..caa8f98
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/B2Api.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2;
+
+import java.io.Closeable;
+
+import org.jclouds.labs.b2.features.AuthorizationApi;
+import org.jclouds.labs.b2.features.BucketApi;
+import org.jclouds.rest.annotations.Delegate;
+
+/** Provides access to Backblaze B2 resources via their REST API. */
+public interface B2Api extends Closeable {
+ @Delegate
+ AuthorizationApi getAuthorizationApi();
+
+ @Delegate
+ BucketApi getBucketApi();
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java b/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java
new file mode 100644
index 0000000..16c4176
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/B2ApiMetadata.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2;
+
+import static org.jclouds.reflect.Reflection2.typeToken;
+
+import java.net.URI;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.jclouds.Constants;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.labs.b2.config.B2HttpApiModule;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+public final class B2ApiMetadata extends BaseHttpApiMetadata {
+ @Override
+ public Builder toBuilder() {
+ return new Builder().fromApiMetadata(this);
+ }
+
+ public B2ApiMetadata() {
+ this(new Builder());
+ }
+
+ protected B2ApiMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = BaseHttpApiMetadata.defaultProperties();
+ properties.setProperty(Constants.PROPERTY_SESSION_INTERVAL, String.valueOf(TimeUnit.HOURS.toSeconds(1)));
+ return properties;
+ }
+
+ public static class Builder extends BaseHttpApiMetadata.Builder<B2Api, Builder> {
+
+ protected Builder() {
+ super(B2Api.class);
+ id("b2")
+ .name("Backblaze B2 API")
+ .identityName("Account Id")
+ .credentialName("Application Key")
+ .documentation(URI.create("https://www.backblaze.com/b2/docs/"))
+ .defaultEndpoint("https://api.backblaze.com/")
+ .defaultProperties(B2ApiMetadata.defaultProperties())
+ .view(typeToken(BlobStoreContext.class))
+ .defaultModules(ImmutableSet.<Class<? extends Module>>of(
+ B2HttpApiModule.class));
+ }
+
+ @Override
+ public B2ApiMetadata build() {
+ return new B2ApiMetadata(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+
+ @Override
+ public Builder fromApiMetadata(ApiMetadata in) {
+ return this;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java b/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java
new file mode 100644
index 0000000..037fd9f
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/B2ProviderMetadata.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2;
+
+import java.util.Properties;
+
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(ProviderMetadata.class)
+public final class B2ProviderMetadata extends BaseProviderMetadata {
+ @Override
+ public Builder toBuilder() {
+ return new Builder().fromProviderMetadata(this);
+ }
+
+ public B2ProviderMetadata() {
+ this(new Builder());
+ }
+
+ protected B2ProviderMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = B2ApiMetadata.defaultProperties();
+ return properties;
+ }
+
+ public static class Builder extends BaseProviderMetadata.Builder {
+
+ protected Builder() {
+ id("b2")
+ .name("Backblaze B2")
+ .apiMetadata(new B2ApiMetadata())
+ .endpoint("https://api.backblaze.com/")
+ .defaultProperties(B2ProviderMetadata.defaultProperties());
+ }
+
+ @Override
+ public B2ProviderMetadata build() {
+ return new B2ProviderMetadata(this);
+ }
+
+ @Override
+ public Builder fromProviderMetadata(ProviderMetadata in) {
+ return this;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/B2ResponseException.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/B2ResponseException.java b/b2/src/main/java/org/jclouds/b2/B2ResponseException.java
new file mode 100644
index 0000000..9ab60ed
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/B2ResponseException.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2;
+
+import org.jclouds.labs.b2.domain.B2Error;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+
+import com.google.common.base.Preconditions;
+
+public final class B2ResponseException extends HttpResponseException {
+ private final B2Error error;
+
+ public B2ResponseException(HttpCommand command, HttpResponse response, B2Error error) {
+ super("request " + command.getCurrentRequest().getRequestLine() + " failed with code " + response.getStatusCode()
+ + ", error: " + Preconditions.checkNotNull(error, "error").toString(), command, response);
+ this.error = error;
+ }
+
+ public B2Error getError() {
+ return error;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java b/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java
new file mode 100644
index 0000000..a266d31
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/config/B2HttpApiModule.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.config;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.collect.Memoized;
+import org.jclouds.labs.b2.B2Api;
+import org.jclouds.labs.b2.domain.Authorization;
+import org.jclouds.labs.b2.filters.RequestAuthorization;
+import org.jclouds.labs.b2.handlers.ParseB2ErrorFromJsonContent;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.config.HttpApiModule;
+import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
+
+import com.google.common.base.Supplier;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+
+/** Configures the mappings. Installs the Object and Parser modules. */
+@ConfiguresHttpApi
+public final class B2HttpApiModule extends HttpApiModule<B2Api> {
+ @Override
+ protected void configure() {
+ super.configure();
+ bind(RequestAuthorization.class).in(Scopes.SINGLETON);
+ }
+
+ @Override
+ protected void bindErrorHandlers() {
+ bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ParseB2ErrorFromJsonContent.class);
+ bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ParseB2ErrorFromJsonContent.class);
+ bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ParseB2ErrorFromJsonContent.class);
+ }
+
+ @Provides
+ @Singleton
+ static Supplier<Authorization> provideAuthorizationSupplier(final B2Api b2Api) {
+ return new Supplier<Authorization>() {
+ @Override
+ public Authorization get() {
+ return b2Api.getAuthorizationApi().authorizeAccount();
+ }
+ };
+ }
+
+ @Provides
+ @Singleton
+ @Memoized
+ static Supplier<Authorization> provideAuthorizationCache(
+ AtomicReference<AuthorizationException> authException,
+ @Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
+ Supplier<Authorization> uncached) {
+ return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(
+ authException, uncached, seconds, TimeUnit.SECONDS);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/domain/Authorization.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/Authorization.java b/b2/src/main/java/org/jclouds/b2/domain/Authorization.java
new file mode 100644
index 0000000..d95d028
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/Authorization.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.domain;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Authorization {
+ public abstract String accountId();
+ public abstract String apiUrl();
+ public abstract String authorizationToken();
+ public abstract String downloadUrl();
+ public abstract long minimumPartSize();
+
+ @SerializedNames({"accountId", "apiUrl", "authorizationToken", "downloadUrl", "minimumPartSize"})
+ public static Authorization create(String accountId, String apiUrl, String authorizationToken, String downloadUrl, long minimumPartSize) {
+ return new AutoValue_Authorization(accountId, apiUrl, authorizationToken, downloadUrl, minimumPartSize);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/domain/B2Error.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/B2Error.java b/b2/src/main/java/org/jclouds/b2/domain/B2Error.java
new file mode 100644
index 0000000..a17ecb9
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/B2Error.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.domain;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class B2Error {
+ public abstract String code();
+ public abstract String message();
+ public abstract int status();
+
+ @SerializedNames({ "code", "message", "status" })
+ public static B2Error create(String code, String message, int status) {
+ return new AutoValue_B2Error(code, message, status);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/domain/Bucket.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/Bucket.java b/b2/src/main/java/org/jclouds/b2/domain/Bucket.java
new file mode 100644
index 0000000..6aff182
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/Bucket.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.domain;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Bucket {
+ public abstract String bucketId();
+ public abstract String accountId();
+ public abstract String bucketName();
+ public abstract BucketType bucketType();
+
+ @SerializedNames({"bucketId", "accountId", "bucketName", "bucketType"})
+ public static Bucket create(String bucketId, String accountId, String bucketName, BucketType bucketType) {
+ return new AutoValue_Bucket(bucketId, accountId, bucketName, bucketType);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/domain/BucketList.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/BucketList.java b/b2/src/main/java/org/jclouds/b2/domain/BucketList.java
new file mode 100644
index 0000000..4be4596
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/BucketList.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.domain;
+
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class BucketList {
+ public abstract List<Bucket> buckets();
+
+ @SerializedNames({"buckets"})
+ public static BucketList create(List<Bucket> buckets) {
+ return new AutoValue_BucketList(ImmutableList.copyOf(buckets));
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/domain/BucketType.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/domain/BucketType.java b/b2/src/main/java/org/jclouds/b2/domain/BucketType.java
new file mode 100644
index 0000000..61b0481
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/domain/BucketType.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.domain;
+
+import com.google.common.base.CaseFormat;
+
+public enum BucketType {
+ ALL_PUBLIC,
+ ALL_PRIVATE,
+ SNAPSHOT;
+
+ public static BucketType fromValue(String symbol) {
+ return BucketType.valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, symbol));
+ }
+
+ @Override
+ public String toString() {
+ return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name());
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java b/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java
new file mode 100644
index 0000000..9789541
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/features/AuthorizationApi.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.jclouds.http.filters.BasicAuthentication;
+import org.jclouds.labs.b2.domain.Authorization;
+import org.jclouds.rest.annotations.RequestFilters;
+
+public interface AuthorizationApi {
+ @Named("b2_authorize_account")
+ @GET
+ @Path("/b2api/v1/b2_authorize_account")
+ @RequestFilters(BasicAuthentication.class)
+ @Consumes(APPLICATION_JSON)
+ Authorization authorizeAccount();
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/features/BucketApi.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/features/BucketApi.java b/b2/src/main/java/org/jclouds/b2/features/BucketApi.java
new file mode 100644
index 0000000..6b341bd
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/features/BucketApi.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.blobstore.attr.BlobScope;
+import org.jclouds.labs.b2.domain.Bucket;
+import org.jclouds.labs.b2.domain.BucketList;
+import org.jclouds.labs.b2.domain.BucketType;
+import org.jclouds.labs.b2.filters.RequestAuthorization;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.PayloadParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+@RequestFilters(RequestAuthorization.class)
+@BlobScope(CONTAINER)
+@Consumes(APPLICATION_JSON)
+@Produces(APPLICATION_JSON)
+public interface BucketApi {
+ @Named("b2_create_bucket")
+ @POST
+ @Path("/b2api/v1/b2_create_bucket")
+ @MapBinder(BindToJsonPayload.class)
+ @PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
+ Bucket createBucket(@PayloadParam("bucketName") String bucketName, @PayloadParam("bucketType") BucketType bucketType);
+
+ @Named("b2_delete_bucket")
+ @POST
+ @Path("/b2api/v1/b2_delete_bucket")
+ @MapBinder(BindToJsonPayload.class)
+ @PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
+ @Fallback(NullOnNotFoundOr404.class)
+ Bucket deleteBucket(@PayloadParam("bucketId") String bucketId);
+
+ @Named("b2_update_bucket")
+ @POST
+ @Path("/b2api/v1/b2_update_bucket")
+ @MapBinder(BindToJsonPayload.class)
+ @PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
+ Bucket updateBucket(@PayloadParam("bucketId") String bucketId, @PayloadParam("bucketType") BucketType bucketType);
+
+ @Named("b2_list_buckets")
+ @POST
+ @Path("/b2api/v1/b2_list_buckets")
+ @MapBinder(BindToJsonPayload.class)
+ @PayloadParams(keys = {"accountId"}, values = {"{jclouds.identity}"})
+ BucketList listBuckets();
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java b/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java
new file mode 100644
index 0000000..ce54199
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/filters/RequestAuthorization.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.filters;
+
+import java.net.URI;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequestFilter;
+import org.jclouds.labs.b2.domain.Authorization;
+import org.jclouds.location.Provider;
+
+import com.google.common.base.Supplier;
+import com.google.common.net.HttpHeaders;
+
+@Singleton
+public final class RequestAuthorization implements HttpRequestFilter {
+ private final Supplier<Credentials> creds;
+ private final Supplier<Authorization> auth;
+
+ @Inject
+ RequestAuthorization(@Provider Supplier<Credentials> creds, @Memoized Supplier<Authorization> auth) {
+ this.creds = creds;
+ this.auth = auth;
+ }
+
+ @Override
+ public HttpRequest filter(HttpRequest request) throws HttpException {
+ Credentials creds = this.creds.get();
+ Authorization auth = this.auth.get();
+
+ // Replace with API URL
+ URI endpoint = request.getEndpoint();
+ endpoint = URI.create(auth.apiUrl() +
+ (endpoint.getPort() == -1 ? "" : ":" + endpoint.getPort()) +
+ endpoint.getPath() +
+ (endpoint.getQuery() == null ? "" : "?" + endpoint.getQuery()));
+
+ request = request.toBuilder()
+ .endpoint(endpoint)
+ .replaceHeader(HttpHeaders.AUTHORIZATION, auth.authorizationToken())
+ .build();
+ return request;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java
----------------------------------------------------------------------
diff --git a/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java b/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java
new file mode 100644
index 0000000..f042478
--- /dev/null
+++ b/b2/src/main/java/org/jclouds/b2/handlers/ParseB2ErrorFromJsonContent.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.handlers;
+
+import org.jclouds.blobstore.ContainerNotFoundException;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.labs.b2.B2ResponseException;
+import org.jclouds.labs.b2.domain.B2Error;
+
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+
+public final class ParseB2ErrorFromJsonContent extends ParseJson<B2Error> implements HttpErrorHandler {
+ @Inject
+ ParseB2ErrorFromJsonContent(Json json) {
+ super(json, TypeLiteral.get(B2Error.class));
+ }
+
+ private static Exception refineException(B2Error error, Exception exception) {
+ if ("bad_bucket_id".equals(error.code())) {
+ return new ContainerNotFoundException(exception);
+ } else if ("bad_json".equals(error.code())) {
+ return new IllegalArgumentException(error.message(), exception);
+ } else {
+ return exception;
+ }
+ }
+
+ @Override
+ public void handleError(HttpCommand command, HttpResponse response) {
+ B2Error error = this.apply(response);
+ Exception exception = refineException(error, new B2ResponseException(command, response, error));
+ command.setException(exception);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java
----------------------------------------------------------------------
diff --git a/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java b/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java
new file mode 100644
index 0000000..91c2f1a
--- /dev/null
+++ b/b2/src/test/java/org/jclouds/b2/B2ProviderMetadataTest.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2;
+
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "B2ProviderMetadataTest")
+public final class B2ProviderMetadataTest extends BaseProviderMetadataTest {
+ public B2ProviderMetadataTest() {
+ super(new B2ProviderMetadata(), new B2ApiMetadata());
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java
----------------------------------------------------------------------
diff --git a/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java b/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java
new file mode 100644
index 0000000..46db59f
--- /dev/null
+++ b/b2/src/test/java/org/jclouds/b2/features/BucketApiLiveTest.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.features;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
+
+import java.util.Random;
+
+import org.jclouds.labs.b2.domain.Bucket;
+import org.jclouds.labs.b2.domain.BucketList;
+import org.jclouds.labs.b2.domain.BucketType;
+import org.jclouds.labs.b2.internal.BaseB2ApiLiveTest;
+import org.testng.annotations.Test;
+
+public final class BucketApiLiveTest extends BaseB2ApiLiveTest {
+ private static final String BUCKET_NAME = "jcloudstestbucket" + new Random().nextInt(Integer.MAX_VALUE);
+
+ @Test(groups = "live")
+ public void testCreateBucket() {
+ BucketApi bucketApi = api.getBucketApi();
+
+ Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
+ try {
+ assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
+ assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+ } finally {
+ response = bucketApi.deleteBucket(response.bucketId());
+ assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
+ assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+ }
+ }
+
+ @Test(groups = "live")
+ public void testDeleteAlreadyDeletedBucket() {
+ BucketApi bucketApi = api.getBucketApi();
+
+ Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
+ response = bucketApi.deleteBucket(response.bucketId());
+
+ response = bucketApi.deleteBucket(response.bucketId());
+ assertThat(response).isNull();
+ }
+
+ @Test(groups = "live")
+ public void testDeleteInvalidBucketId() {
+ BucketApi bucketApi = api.getBucketApi();
+
+ try {
+ bucketApi.deleteBucket("4a48fe8875c6214145260818");
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (IllegalArgumentException iae) {
+ assertThat(iae.getMessage()).isEqualTo("bucketId not valid for account");
+ }
+ }
+
+ @Test(groups = "live")
+ public void testUpdateBucket() {
+ BucketApi bucketApi = api.getBucketApi();
+
+ Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
+ try {
+ response = bucketApi.updateBucket(response.bucketId(), BucketType.ALL_PUBLIC);
+ assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
+ assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PUBLIC);
+ } finally {
+ response = bucketApi.deleteBucket(response.bucketId());
+ assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
+ }
+ }
+
+ @Test(groups = "live")
+ public void testListBuckets() {
+ BucketApi bucketApi = api.getBucketApi();
+
+ Bucket response = bucketApi.createBucket(BUCKET_NAME, BucketType.ALL_PRIVATE);
+ try {
+ boolean found = false;
+ BucketList buckets = bucketApi.listBuckets();
+ for (Bucket bucket : buckets.buckets()) {
+ if (bucket.bucketName().equals(BUCKET_NAME)) {
+ assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+ found = true;
+ }
+ }
+ assertThat(found).isTrue();
+ } finally {
+ response = bucketApi.deleteBucket(response.bucketId());
+ assertThat(response.bucketName()).isEqualTo(BUCKET_NAME);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java
----------------------------------------------------------------------
diff --git a/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java b/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java
new file mode 100644
index 0000000..35c4188
--- /dev/null
+++ b/b2/src/test/java/org/jclouds/b2/features/BucketApiMockTest.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.features;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Set;
+import java.util.Properties;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.labs.b2.B2Api;
+import org.jclouds.labs.b2.domain.Bucket;
+import org.jclouds.labs.b2.domain.BucketList;
+import org.jclouds.labs.b2.domain.BucketType;
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.inject.Module;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+@Test(groups = "unit", testName = "BucketApiMockTest")
+public final class BucketApiMockTest {
+ private final Set<Module> modules = ImmutableSet.<Module> of(
+ new ExecutorServiceModule(MoreExecutors.sameThreadExecutor()));
+
+ public void testCreateBucket() throws Exception {
+ MockWebServer server = createMockWebServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/bucket.json")));
+
+ try {
+ BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
+ Bucket response = api.createBucket("any_name_you_pick", BucketType.ALL_PRIVATE);
+ assertThat(response.bucketId()).isEqualTo("4a48fe8875c6214145260818");
+ assertThat(response.bucketName()).isEqualTo("any_name_you_pick");
+ assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+
+ assertThat(server.getRequestCount()).isEqualTo(2);
+ assertAuthentication(server);
+ assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_create_bucket", "/create_bucket_request.json");
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ public void testDeleteBucket() throws Exception {
+ MockWebServer server = createMockWebServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/bucket.json")));
+
+ try {
+ BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
+ Bucket response = api.deleteBucket("4a48fe8875c6214145260818");
+ assertThat(response.bucketId()).isEqualTo("4a48fe8875c6214145260818");
+ assertThat(response.bucketName()).isEqualTo("any_name_you_pick");
+ assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+
+ assertThat(server.getRequestCount()).isEqualTo(2);
+ assertAuthentication(server);
+ assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_delete_bucket", "/delete_bucket_request.json");
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ public void testDeleteAlreadyDeletedBucket() throws Exception {
+ MockWebServer server = createMockWebServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+ server.enqueue(new MockResponse().setResponseCode(400).setBody(stringFromResource("/delete_bucket_already_deleted_response.json")));
+
+ try {
+ BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
+ Bucket response = api.deleteBucket("4a48fe8875c6214145260818");
+ assertThat(response).isNull();
+
+ assertThat(server.getRequestCount()).isEqualTo(2);
+ assertAuthentication(server);
+ assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_delete_bucket", "/delete_bucket_request.json");
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ public void testUpdateBucket() throws Exception {
+ MockWebServer server = createMockWebServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/bucket.json")));
+
+ try {
+ BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
+ Bucket response = api.updateBucket("4a48fe8875c6214145260818", BucketType.ALL_PRIVATE);
+ assertThat(response.bucketId()).isEqualTo("4a48fe8875c6214145260818");
+ assertThat(response.bucketName()).isEqualTo("any_name_you_pick");
+ assertThat(response.bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+
+ assertThat(server.getRequestCount()).isEqualTo(2);
+ assertAuthentication(server);
+ assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_update_bucket", "/update_bucket_request.json");
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ public void testListBuckets() throws Exception {
+ MockWebServer server = createMockWebServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/authorize_account_response.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/list_buckets_response.json")));
+
+ try {
+ BucketApi api = api(server.getUrl("/").toString(), "b2").getBucketApi();
+ BucketList response = api.listBuckets();
+
+ assertThat(response.buckets()).hasSize(3);
+
+ assertThat(response.buckets().get(0).bucketName()).isEqualTo("Kitten Videos");
+ assertThat(response.buckets().get(0).bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+
+ assertThat(response.buckets().get(1).bucketName()).isEqualTo("Puppy Videos");
+ assertThat(response.buckets().get(1).bucketType()).isEqualTo(BucketType.ALL_PUBLIC);
+
+ assertThat(response.buckets().get(2).bucketName()).isEqualTo("Vacation Pictures");
+ assertThat(response.buckets().get(2).bucketType()).isEqualTo(BucketType.ALL_PRIVATE);
+
+ assertThat(server.getRequestCount()).isEqualTo(2);
+ assertAuthentication(server);
+ assertRequest(server.takeRequest(), "POST", "/b2api/v1/b2_list_buckets", "/list_buckets_request.json");
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ public B2Api api(String uri, String provider, Properties overrides) {
+ return ContextBuilder.newBuilder(provider)
+ .credentials("ACCOUNT_ID", "APPLICATION_KEY")
+ .endpoint(uri)
+ .overrides(overrides)
+ .modules(modules)
+ .buildApi(B2Api.class);
+ }
+
+ public B2Api api(String uri, String provider) {
+ return api(uri, provider, new Properties());
+ }
+
+ public static MockWebServer createMockWebServer() throws IOException {
+ MockWebServer server = new MockWebServer();
+ server.play();
+ URL url = server.getUrl("");
+ return server;
+ }
+
+ public void assertAuthentication(MockWebServer server) {
+ assertThat(server.getRequestCount()).isGreaterThanOrEqualTo(1);
+ try {
+ assertThat(server.takeRequest().getRequestLine()).isEqualTo("GET /b2api/v1/b2_authorize_account HTTP/1.1");
+ } catch (InterruptedException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ /**
+ * Ensures the request has a json header for the proper REST methods.
+ *
+ * @param request
+ * @param method
+ * The request method (such as GET).
+ * @param path
+ * The path requested for this REST call.
+ * @see RecordedRequest
+ */
+ public void assertRequest(RecordedRequest request, String method, String path) {
+ assertThat(request.getMethod()).isEqualTo(method);
+ assertThat(request.getPath()).isEqualTo(path);
+ }
+
+ /**
+ * Ensures the request is json and has the same contents as the resource
+ * file provided.
+ *
+ * @param request
+ * @param method
+ * The request method (such as GET).
+ * @param resourceLocation
+ * The location of the resource file. Contents will be compared to
+ * the request body as JSON.
+ * @see RecordedRequest
+ */
+ public void assertRequest(RecordedRequest request, String method, String path, String resourceLocation) {
+ assertRequest(request, method, path);
+ assertContentTypeIsJson(request);
+ JsonParser parser = new JsonParser();
+ JsonElement requestJson;
+ try {
+ requestJson = parser.parse(new String(request.getBody(), Charsets.UTF_8));
+ } catch (Exception e) {
+ throw Throwables.propagate(e);
+ }
+ JsonElement resourceJson = parser.parse(stringFromResource(resourceLocation));
+ assertThat(requestJson).isEqualTo(resourceJson);
+ }
+
+ /**
+ * Ensures the request has a json header.
+ *
+ * @param request
+ * @see RecordedRequest
+ */
+ private void assertContentTypeIsJson(RecordedRequest request) {
+ assertThat(request.getHeaders()).contains("Content-Type: application/json");
+ }
+
+ /**
+ * Get a string from a resource
+ *
+ * @param resourceName
+ * The name of the resource.
+ * @return The content of the resource
+ */
+ public String stringFromResource(String resourceName) {
+ try {
+ return Strings2.toStringAndClose(getClass().getResourceAsStream(resourceName));
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java
----------------------------------------------------------------------
diff --git a/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java b/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java
new file mode 100644
index 0000000..59ac34a
--- /dev/null
+++ b/b2/src/test/java/org/jclouds/b2/internal/BaseB2ApiLiveTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.jclouds.labs.b2.internal;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.labs.b2.B2Api;
+import org.jclouds.labs.b2.B2ApiMetadata;
+
+public class BaseB2ApiLiveTest extends BaseApiLiveTest<B2Api> {
+ protected BaseB2ApiLiveTest() {
+ provider = "b2";
+ }
+
+ @Override
+ protected ApiMetadata createApiMetadata() {
+ return new B2ApiMetadata();
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/authorize_account_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/authorize_account_response.json b/b2/src/test/resources/authorize_account_response.json
new file mode 100644
index 0000000..ba02ea7
--- /dev/null
+++ b/b2/src/test/resources/authorize_account_response.json
@@ -0,0 +1,7 @@
+{
+ "accountId": "YOUR_ACCOUNT_ID",
+ "apiUrl": "http://localhost",
+ "authorizationToken": "2_20150807002553_443e98bf57f978fa58c284f8_24d25d99772e3ba927778b39c9b0198f412d2163_acct",
+ "downloadUrl": "http://localhost",
+ "minimumPartSize": 100000000
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/bucket.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/bucket.json b/b2/src/test/resources/bucket.json
new file mode 100644
index 0000000..50b2bde
--- /dev/null
+++ b/b2/src/test/resources/bucket.json
@@ -0,0 +1,6 @@
+{
+ "bucketId" : "4a48fe8875c6214145260818",
+ "accountId" : "010203040506",
+ "bucketName" : "any_name_you_pick",
+ "bucketType" : "allPrivate"
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/create_bucket_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/create_bucket_request.json b/b2/src/test/resources/create_bucket_request.json
new file mode 100644
index 0000000..8b72ad5
--- /dev/null
+++ b/b2/src/test/resources/create_bucket_request.json
@@ -0,0 +1,5 @@
+{
+ "accountId": "ACCOUNT_ID",
+ "bucketName": "any_name_you_pick",
+ "bucketType": "allPrivate"
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/delete_bucket_already_deleted_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/delete_bucket_already_deleted_response.json b/b2/src/test/resources/delete_bucket_already_deleted_response.json
new file mode 100644
index 0000000..6ac1748
--- /dev/null
+++ b/b2/src/test/resources/delete_bucket_already_deleted_response.json
@@ -0,0 +1,5 @@
+{
+ "status" : 400,
+ "code" : "bad_bucket_id",
+ "message" : "Bucket id 4a48fe8875c6214145260818 does not exist"
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/delete_bucket_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/delete_bucket_request.json b/b2/src/test/resources/delete_bucket_request.json
new file mode 100644
index 0000000..8719353
--- /dev/null
+++ b/b2/src/test/resources/delete_bucket_request.json
@@ -0,0 +1,4 @@
+{
+ "accountId": "ACCOUNT_ID",
+ "bucketId": "4a48fe8875c6214145260818"
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/list_buckets_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/list_buckets_request.json b/b2/src/test/resources/list_buckets_request.json
new file mode 100644
index 0000000..e2854f5
--- /dev/null
+++ b/b2/src/test/resources/list_buckets_request.json
@@ -0,0 +1,3 @@
+{
+ "accountId": "ACCOUNT_ID"
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/list_buckets_response.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/list_buckets_response.json b/b2/src/test/resources/list_buckets_response.json
new file mode 100644
index 0000000..dbf4ad8
--- /dev/null
+++ b/b2/src/test/resources/list_buckets_response.json
@@ -0,0 +1,21 @@
+{
+ "buckets": [
+ {
+ "bucketId": "4a48fe8875c6214145260818",
+ "accountId": "30f20426f0b1",
+ "bucketName" : "Kitten Videos",
+ "bucketType": "allPrivate"
+ },
+ {
+ "bucketId" : "5b232e8875c6214145260818",
+ "accountId": "30f20426f0b1",
+ "bucketName": "Puppy Videos",
+ "bucketType": "allPublic"
+ },
+ {
+ "bucketId": "87ba238875c6214145260818",
+ "accountId": "30f20426f0b1",
+ "bucketName": "Vacation Pictures",
+ "bucketType" : "allPrivate"
+ } ]
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/log4j.xml
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/log4j.xml b/b2/src/test/resources/log4j.xml
new file mode 100644
index 0000000..9b64842
--- /dev/null
+++ b/b2/src/test/resources/log4j.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+ <!--
+ For more configuration infromation and examples see the Apache
+ Log4j website: http://logging.apache.org/log4j/
+ -->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
+ debug="false">
+
+ <!-- A time/date based rolling appender -->
+ <appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
+ <param name="File" value="target/test-data/jclouds-wire.log" />
+ <param name="Append" value="true" />
+
+ <!-- Rollover at midnight each day -->
+ <param name="DatePattern" value="'.'yyyy-MM-dd" />
+
+ <param name="Threshold" value="TRACE" />
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <!-- The default pattern: Date Priority [Category] Message\n -->
+ <param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
+
+ <!--
+ The full pattern: Date MS Priority [Category]
+ (Thread:NDC) Message\n <param name="ConversionPattern"
+ value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
+ -->
+ </layout>
+ </appender>
+
+ <!-- A time/date based rolling appender -->
+ <appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
+ <param name="File" value="target/test-data/jclouds.log" />
+ <param name="Append" value="true" />
+
+ <!-- Rollover at midnight each day -->
+ <param name="DatePattern" value="'.'yyyy-MM-dd" />
+
+ <param name="Threshold" value="TRACE" />
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <!-- The default pattern: Date Priority [Category] Message\n -->
+ <param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
+
+ <!--
+ The full pattern: Date MS Priority [Category]
+ (Thread:NDC) Message\n <param name="ConversionPattern"
+ value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
+ -->
+ </layout>
+ </appender>
+ <!-- A time/date based rolling appender -->
+ <appender name="BLOBSTOREFILE" class="org.apache.log4j.DailyRollingFileAppender">
+ <param name="File" value="target/test-data/jclouds-blobstore.log" />
+ <param name="Append" value="true" />
+ <param name="DatePattern" value="'.'yyyy-MM-dd" />
+ <param name="Threshold" value="TRACE" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
+ </layout>
+ </appender>
+
+ <appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
+ <appender-ref ref="FILE" />
+ </appender>
+
+ <appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
+ <appender-ref ref="WIREFILE" />
+ </appender>
+
+ <appender name="ASYNCBLOBSTORE" class="org.apache.log4j.AsyncAppender">
+ <appender-ref ref="BLOBSTOREFILE" />
+ </appender>
+ <!-- ================ -->
+ <!-- Limit categories -->
+ <!-- ================ -->
+
+ <category name="org.jclouds">
+ <priority value="DEBUG" />
+ <appender-ref ref="ASYNC" />
+ </category>
+
+ <category name="jclouds.headers">
+ <priority value="DEBUG" />
+ <appender-ref ref="ASYNCWIRE" />
+ </category>
+ <!--
+ NOTE enabling this will break stream tests <category
+ name="jclouds.wire"> <priority value="DEBUG" /> <appender-ref
+ ref="ASYNCWIRE" /> </category>
+ -->
+ <category name="jclouds.blobstore">
+ <priority value="DEBUG" />
+ <appender-ref ref="ASYNCBLOBSTORE" />
+ </category>
+ <!-- ======================= -->
+ <!-- Setup the Root category -->
+ <!-- ======================= -->
+
+ <root>
+ <priority value="WARN" />
+ </root>
+
+</log4j:configuration>
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/b2/src/test/resources/update_bucket_request.json
----------------------------------------------------------------------
diff --git a/b2/src/test/resources/update_bucket_request.json b/b2/src/test/resources/update_bucket_request.json
new file mode 100644
index 0000000..64cc475
--- /dev/null
+++ b/b2/src/test/resources/update_bucket_request.json
@@ -0,0 +1,5 @@
+{
+ "accountId": "ACCOUNT_ID",
+ "bucketId": "4a48fe8875c6214145260818",
+ "bucketType": "allPrivate"
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/afca4993/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index cb03655..716eac6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,7 @@
<modules>
<module>azurecompute-arm</module>
<module>azurecompute</module>
+ <module>b2</module>
<module>docker</module>
<module>cdmi</module>
<module>cloudsigma2</module>