Import openstack-swift from labs.

This commit is contained in:
Adrian Cole 2014-10-08 08:18:47 -07:00 committed by Andrew Phillips
parent 8432c66b74
commit 34663f3c28
82 changed files with 8455 additions and 0 deletions

View File

@ -53,6 +53,11 @@
<artifactId>swift</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jclouds.api</groupId>
<artifactId>openstack-swift</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jclouds.api</groupId>
<artifactId>cloudfiles</artifactId>

View File

@ -0,0 +1,10 @@
OpenStack Swift
===============
Swift is a distributed object storage system designed to scale from a single machine to thousands of servers.
Production ready?
Beta
This API is new to jclouds and hence is in Beta. That means we need people to use it and give us feedback. Based on that feedback, minor changes to the interfaces may happen. This code will replace org.jclouds.openstack.swift.SwiftClient in jclouds 2.0 and it is recommended you adopt it sooner than later.

View File

@ -0,0 +1,141 @@
<?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="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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</groupId>
<artifactId>jclouds-project</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../../project/pom.xml</relativePath>
</parent>
<groupId>org.apache.jclouds.api</groupId>
<artifactId>openstack-swift</artifactId>
<name>jclouds openstack-swift api</name>
<description>jclouds components to access an implementation of OpenStack Swift</description>
<packaging>bundle</packaging>
<properties>
<!-- keystone endpoint -->
<test.openstack-swift.endpoint>http://localhost:5000/v2.0/</test.openstack-swift.endpoint>
<test.openstack-swift.api-version>1.0</test.openstack-swift.api-version>
<test.openstack-swift.build-version />
<test.openstack-swift.identity>FIXME_IDENTITY</test.openstack-swift.identity>
<test.openstack-swift.credential>FIXME_CREDENTIALS</test.openstack-swift.credential>
<test.jclouds.keystone.credential-type>passwordCredentials</test.jclouds.keystone.credential-type>
<jclouds.blobstore.httpstream.url>http://archive.apache.org/dist/commons/logging/binaries/commons-logging-1.1.1-bin.tar.gz</jclouds.blobstore.httpstream.url>
<jclouds.blobstore.httpstream.md5>e5de09672af9b386c30a311654d8541a</jclouds.blobstore.httpstream.md5>
<jclouds.osgi.export>org.jclouds.openstack.swift.v1*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.jclouds.api</groupId>
<artifactId>openstack-keystone</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-blobstore</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds.api</groupId>
<artifactId>openstack-keystone</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-blobstore</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.shrinkwrap</groupId>
<artifactId>shrinkwrap-depchain</artifactId>
<version>1.2.0</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds.driver</groupId>
<artifactId>jclouds-slf4j</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</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>
<systemPropertyVariables>
<test.openstack-swift.endpoint>${test.openstack-swift.endpoint}</test.openstack-swift.endpoint>
<test.openstack-swift.api-version>${test.openstack-swift.api-version}</test.openstack-swift.api-version>
<test.openstack-swift.build-version>${test.openstack-swift.build-version}</test.openstack-swift.build-version>
<test.openstack-swift.identity>${test.openstack-swift.identity}</test.openstack-swift.identity>
<test.openstack-swift.credential>${test.openstack-swift.credential}</test.openstack-swift.credential>
<test.jclouds.keystone.credential-type>${test.jclouds.keystone.credential-type}</test.jclouds.keystone.credential-type>
<jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
<jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,48 @@
/*
* 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.openstack.swift.v1;
import org.jclouds.rest.ResourceNotFoundException;
/**
* Thrown when an object cannot be copied.
*
*
* @see {@link SwiftErrorHandler#handleError(HttpCommand, HttpResponse)}
*/
@SuppressWarnings("serial")
public class CopyObjectException extends ResourceNotFoundException {
private String sourcePath;
private String destinationPath;
public CopyObjectException(String sourcePath, String destinationPath, String message) {
super(String.format("Either the source path '%s' or the destination path '%s' was not found. " +
"(message: %s)", sourcePath, destinationPath, message));
this.sourcePath = sourcePath;
this.destinationPath = destinationPath;
}
public String getSourcePath() {
return sourcePath;
}
public String getDestinationPath() {
return destinationPath;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.openstack.swift.v1;
import java.io.Closeable;
import java.util.Set;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.location.Region;
import org.jclouds.location.functions.RegionToEndpoint;
import org.jclouds.openstack.swift.v1.features.AccountApi;
import org.jclouds.openstack.swift.v1.features.BulkApi;
import org.jclouds.openstack.swift.v1.features.ContainerApi;
import org.jclouds.openstack.swift.v1.features.ObjectApi;
import org.jclouds.openstack.swift.v1.features.StaticLargeObjectApi;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
import com.google.common.annotations.Beta;
import com.google.inject.Provides;
/**
* Provides access to the OpenStack Object Storage (Swift) API.
* <p/>
* OpenStack Object Storage is an object-based storage system that stores content and metadata
* as objects. You create, modify, and get objects and metadata using this API.
* <p/>
*/
@Beta
public interface SwiftApi extends Closeable {
@Provides
@Region
Set<String> getConfiguredRegions();
@Delegate
AccountApi getAccountApi(@EndpointParam(parser = RegionToEndpoint.class) String region);
@Delegate
BulkApi getBulkApi(@EndpointParam(parser = RegionToEndpoint.class) String region);
@Delegate
ContainerApi getContainerApi(@EndpointParam(parser = RegionToEndpoint.class) String region);
@Delegate
@Path("/{containerName}")
ObjectApi getObjectApi(@EndpointParam(parser = RegionToEndpoint.class) String region,
@PathParam("containerName") String containerName);
@Delegate
@Path("/{containerName}")
StaticLargeObjectApi getStaticLargeObjectApi(@EndpointParam(parser = RegionToEndpoint.class) String region,
@PathParam("containerName") String containerName);
}

View File

@ -0,0 +1,99 @@
/*
* 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.openstack.swift.v1;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.SERVICE_TYPE;
import static org.jclouds.reflect.Reflection2.typeToken;
import java.net.URI;
import java.util.Properties;
import org.jclouds.openstack.keystone.v2_0.config.AuthenticationApiModule;
import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneAuthenticationModule.RegionModule;
import org.jclouds.openstack.swift.v1.blobstore.RegionScopedBlobStoreContext;
import org.jclouds.openstack.swift.v1.blobstore.config.SignUsingTemporaryUrls;
import org.jclouds.openstack.swift.v1.blobstore.config.SwiftBlobStoreContextModule;
import org.jclouds.openstack.swift.v1.config.SwiftHttpApiModule;
import org.jclouds.openstack.swift.v1.config.SwiftTypeAdapters;
import org.jclouds.openstack.v2_0.ServiceType;
import org.jclouds.rest.internal.BaseHttpApiMetadata;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
/**
* Implementation of {@link ApiMetadata} for the Swift API.
*/
public class SwiftApiMetadata extends BaseHttpApiMetadata<SwiftApi> {
@Override
public Builder toBuilder() {
return new Builder().fromApiMetadata(this);
}
public SwiftApiMetadata() {
this(new Builder());
}
protected SwiftApiMetadata(Builder builder) {
super(builder);
}
public static Properties defaultProperties() {
Properties properties = BaseHttpApiMetadata.defaultProperties();
properties.setProperty(SERVICE_TYPE, ServiceType.OBJECT_STORE);
properties.setProperty(CREDENTIAL_TYPE, CredentialTypes.PASSWORD_CREDENTIALS);
return properties;
}
public static class Builder extends BaseHttpApiMetadata.Builder<SwiftApi, Builder> {
protected Builder() {
id("openstack-swift")
.name("OpenStack Swift API")
.identityName("${tenantName}:${userName} or ${userName}, if your keystone supports a default tenant")
.credentialName("${password}")
.documentation(URI.create("http://docs.openstack.org/api/openstack-object-storage/1.0/content/ch_object-storage-dev-overview.html"))
.version("1")
.endpointName("Keystone base url ending in /v2.0/")
.defaultEndpoint("http://localhost:5000/v2.0/")
.defaultProperties(SwiftApiMetadata.defaultProperties())
.view(typeToken(RegionScopedBlobStoreContext.class))
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
.add(AuthenticationApiModule.class)
.add(KeystoneAuthenticationModule.class)
.add(RegionModule.class)
.add(SwiftTypeAdapters.class)
.add(SwiftHttpApiModule.class)
.add(SwiftBlobStoreContextModule.class)
.add(SignUsingTemporaryUrls.class).build());
}
@Override
public SwiftApiMetadata build() {
return new SwiftApiMetadata(this);
}
@Override
protected Builder self() {
return this;
}
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.openstack.swift.v1;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Throwables.propagate;
import static org.jclouds.http.HttpUtils.contains404;
import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull;
import org.jclouds.Fallback;
public final class SwiftFallbacks {
public static final class TrueOn404FalseOn409 implements Fallback<Boolean> {
@Override
public Boolean createOrPropagate(Throwable t) throws Exception {
if (contains404(checkNotNull(t, "throwable")))
return true;
if (returnValueOnCodeOrNull(t, false, equalTo(409)) != null)
return false;
throw propagate(t);
}
}
private SwiftFallbacks() {
throw new AssertionError("intentionally unimplemented");
}
}

View File

@ -0,0 +1,89 @@
/*
* 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.openstack.swift.v1;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Suppliers.memoizeWithExpiration;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.io.BaseEncoding.base16;
import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.SECONDS;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.jclouds.openstack.swift.v1.features.AccountApi;
import com.google.common.base.Supplier;
/**
* Use this utility to create temporary urls.
*/
public class TemporaryUrlSigner {
public static TemporaryUrlSigner checkApiEvery(final AccountApi api, long seconds) {
Supplier<String> keySupplier = memoizeWithExpiration(new TemporaryUrlKeyFromAccount(api), seconds, SECONDS);
return new TemporaryUrlSigner(keySupplier);
}
private final Supplier<String> keySupplier;
TemporaryUrlSigner(Supplier<String> keySupplier) {
this.keySupplier = keySupplier;
}
public String sign(String method, String path, long expirationTimestampSeconds) {
checkNotNull(method, "method");
checkNotNull(path, "path");
checkArgument(expirationTimestampSeconds > 0, "expirationTimestamp must be a unix epoch timestamp");
String hmacBody = format("%s\n%s\n%s", method, expirationTimestampSeconds, path);
return base16().lowerCase().encode(hmacSHA1(hmacBody));
}
byte[] hmacSHA1(String data) {
try {
String key = keySupplier.get();
checkState(key != null, "%s returned a null temporaryUrlKey!", keySupplier);
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(key.getBytes(UTF_8), "HmacSHA1"));
return mac.doFinal(data.getBytes(UTF_8));
} catch (Exception e) {
throw propagate(e);
}
}
static class TemporaryUrlKeyFromAccount implements Supplier<String> {
private final AccountApi api;
private TemporaryUrlKeyFromAccount(AccountApi api) {
this.api = checkNotNull(api, "accountApi");
}
@Override
public String get() {
return api.get().getTemporaryUrlKey().orNull();
}
@Override
public String toString() {
return format("get().getTemporaryUrlKey() using %s", api);
}
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.openstack.swift.v1.binders;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_METADATA_PREFIX;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableMultimap.Builder;
/**
* Will lower-case header keys due to a swift implementation to return headers
* in a different case than sent. ex.
*
* <pre>
* >> X-Account-Meta-MyDelete1: foo
* >> X-Account-Meta-MyDelete2: bar
* </pre>
*
* results in:
*
* <pre>
* << X-Account-Meta-Mydelete1: foo
* << X-Account-Meta-Mydelete2: bar
* </pre>
*
* <h4>Note</h4> <br/>
* HTTP response headers keys are known to be case-insensitive, but this
* practice of mixing up case will prevent metadata keys such as those in
* Turkish from working.
*/
public class BindMetadataToHeaders implements Binder {
public static class BindAccountMetadataToHeaders extends BindMetadataToHeaders {
BindAccountMetadataToHeaders() {
super(ACCOUNT_METADATA_PREFIX);
}
}
public static class BindRemoveAccountMetadataToHeaders extends BindMetadataToHeaders.ForRemoval {
BindRemoveAccountMetadataToHeaders() {
super(ACCOUNT_METADATA_PREFIX);
}
}
public static class BindContainerMetadataToHeaders extends BindMetadataToHeaders {
BindContainerMetadataToHeaders() {
super(CONTAINER_METADATA_PREFIX);
}
}
public static class BindRemoveContainerMetadataToHeaders extends BindMetadataToHeaders.ForRemoval {
BindRemoveContainerMetadataToHeaders() {
super(CONTAINER_METADATA_PREFIX);
}
}
public static class BindObjectMetadataToHeaders extends BindMetadataToHeaders {
BindObjectMetadataToHeaders() {
super(OBJECT_METADATA_PREFIX);
}
}
public static class BindRemoveObjectMetadataToHeaders extends BindMetadataToHeaders.ForRemoval {
BindRemoveObjectMetadataToHeaders() {
super(OBJECT_METADATA_PREFIX);
}
}
/**
* @see <a
* href="http://docs.openstack.org/api/openstack-object-storage/1.0/content/delete-account-metadata.html">documentation</a>
*/
public abstract static class ForRemoval extends BindMetadataToHeaders {
ForRemoval(String metadataPrefix) {
super(metadataPrefix);
}
@Override
protected void putMetadata(Builder<String, String> headers, String key, String value) {
headers.put(String.format("x-remove%s", key.substring(1)), "ignored");
}
}
private final String metadataPrefix;
public BindMetadataToHeaders(String metadataPrefix) {
this.metadataPrefix = checkNotNull(metadataPrefix, "metadataPrefix");
}
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
checkNotNull(request, "request");
checkArgument(input instanceof Map<?, ?>, "input must be a non-null java.util.Map!");
Map<String, String> metadata = Map.class.cast(input);
ImmutableMultimap<String, String> headers = toHeaders(metadata);
return (R) request.toBuilder().replaceHeaders(headers).build();
}
protected void putMetadata(Builder<String, String> headers, String key, String value) {
headers.put(key, value);
}
public ImmutableMultimap<String, String> toHeaders(Map<String, String> metadata) {
Builder<String, String> builder = ImmutableMultimap.<String, String> builder();
for (Entry<String, String> keyVal : metadata.entrySet()) {
String keyInLowercase = keyVal.getKey().toLowerCase();
if (keyVal.getKey().startsWith(metadataPrefix)) {
putMetadata(builder, keyInLowercase, keyVal.getValue());
} else {
putMetadata(builder, String.format("%s%s", metadataPrefix, keyInLowercase), keyVal.getValue());
}
}
return builder.build();
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.openstack.swift.v1.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.net.HttpHeaders.ETAG;
import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_DELETE_AT;
import java.util.Date;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequest.Builder;
import org.jclouds.io.Payload;
import org.jclouds.rest.Binder;
import com.google.common.hash.HashCode;
public class SetPayload implements Binder {
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
Builder<?> builder = request.toBuilder();
Payload payload = Payload.class.cast(input);
if (payload.getContentMetadata().getContentType() == null) {
// TODO: use `X-Detect-Content-Type` here. Should be configurable via a property.
payload.getContentMetadata().setContentType(MediaType.APPLICATION_OCTET_STREAM);
}
Long contentLength = payload.getContentMetadata().getContentLength();
if (contentLength != null && contentLength >= 0) {
checkArgument(contentLength <= 5l * 1024 * 1024 * 1024, "maximum size for put object is 5GB, %s",
contentLength);
} else {
builder.replaceHeader(TRANSFER_ENCODING, "chunked").build();
}
HashCode md5 = payload.getContentMetadata().getContentMD5AsHashCode();
if (md5 != null) {
// Swift will validate the md5, if placed as an ETag header
builder.replaceHeader(ETAG, base16().lowerCase().encode(md5.asBytes()));
}
Date expires = payload.getContentMetadata().getExpires();
if (expires != null) {
builder.addHeader(OBJECT_DELETE_AT,
String.valueOf(MILLISECONDS.toSeconds(expires.getTime()))).build();
}
return (R) builder.payload(payload).build();
}
}

View File

@ -0,0 +1,154 @@
/*
* 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.openstack.swift.v1.blobstore;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Set;
import javax.inject.Inject;
import org.jclouds.Context;
import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.attr.ConsistencyModel;
import org.jclouds.internal.BaseView;
import org.jclouds.location.Provider;
import org.jclouds.location.Region;
import org.jclouds.rest.Utils;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.reflect.TypeToken;
/**
* Implementation of {@link BlobStoreContext} which allows you to employ
* multiple regions.
*
* Example.
*
* <pre>
* ctx = contextBuilder.buildView(RegionScopedBlobStoreContext.class);
*
* Set&lt;String&gt; regionIds = ctx.getConfiguredRegions();
*
* // isolated to a specific region
* BlobStore texasBlobStore = ctx.getBlobStore(&quot;US-TX&quot;);
* BlobStore virginiaBlobStore = ctx.getBlobStore(&quot;US-VA&quot;);
* </pre>
*/
public class RegionScopedBlobStoreContext extends BaseView implements BlobStoreContext {
/**
* @return regions supported in this context.
*/
public Set<String> getConfiguredRegions() {
return regionIds.get();
}
/**
* @param regionId
* valid region id from {@link #getConfiguredRegions()}
* @throws IllegalArgumentException
* if {@code regionId} was invalid.
*/
public BlobStore getBlobStore(String regionId) {
checkRegionId(regionId);
return blobStore.apply(regionId);
}
/**
* @param regionId
* valid region id from {@link #getConfiguredRegions()}
* @throws IllegalArgumentException
* if {@code regionId} was invalid.
*/
public BlobRequestSigner getSigner(String regionId) {
checkRegionId(regionId);
return blobRequestSigner.apply(regionId);
}
protected void checkRegionId(String regionId) {
checkArgument(getConfiguredRegions().contains(checkNotNull(regionId, "regionId was null")), "region %s not in %s",
regionId, getConfiguredRegions());
}
private final Supplier<Set<String>> regionIds;
private final Supplier<String> implicitRegionId;
// factory functions are decoupled so that you can exchange how requests are
// signed or decorate without a class hierarchy dependency
private final Function<String, BlobStore> blobStore;
private final Function<String, BlobRequestSigner> blobRequestSigner;
private final Utils utils;
private final ConsistencyModel consistencyModel;
@Inject
public RegionScopedBlobStoreContext(@Provider Context backend, @Provider TypeToken<? extends Context> backendType,
@Region Supplier<Set<String>> regionIds, @Region Supplier<String> implicitRegionId,
Function<String, BlobStore> blobStore, Function<String, BlobRequestSigner> blobRequestSigner, Utils utils,
ConsistencyModel consistencyModel) {
super(backend, backendType);
this.regionIds = checkNotNull(regionIds, "regionIds");
this.implicitRegionId = checkNotNull(implicitRegionId, "implicitRegionId");
this.blobStore = checkNotNull(blobStore, "blobStore");
this.blobRequestSigner = checkNotNull(blobRequestSigner, "blobRequestSigner");
this.utils = checkNotNull(utils, "utils");
this.consistencyModel = checkNotNull(consistencyModel, "consistencyModel");
}
@Override
public ConsistencyModel getConsistencyModel() {
return consistencyModel;
}
@Override
public BlobStore getBlobStore() {
return getBlobStore(implicitRegionId.get());
}
@Override
public BlobRequestSigner getSigner() {
return getSigner(implicitRegionId.get());
}
@Override
public Utils utils() {
return utils;
}
@Override
public void close() {
delegate().close();
}
public int hashCode() {
return delegate().hashCode();
}
@Override
public String toString() {
return delegate().toString();
}
@Override
public boolean equals(Object obj) {
return delegate().equals(obj);
}
}

View File

@ -0,0 +1,312 @@
/*
* 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.openstack.swift.v1.blobstore;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.tryFind;
import static com.google.common.collect.Lists.transform;
import static org.jclouds.blobstore.options.ListContainerOptions.Builder.recursive;
import static org.jclouds.location.predicates.LocationPredicates.idEquals;
import static org.jclouds.openstack.swift.v1.options.PutOptions.Builder.metadata;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobBuilder;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.domain.internal.BlobBuilderImpl;
import org.jclouds.blobstore.domain.internal.BlobImpl;
import org.jclouds.blobstore.domain.internal.PageSetImpl;
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
import org.jclouds.blobstore.options.CreateContainerOptions;
import org.jclouds.blobstore.options.GetOptions;
import org.jclouds.blobstore.options.ListContainerOptions;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.blobstore.strategy.ClearListStrategy;
import org.jclouds.collect.Memoized;
import org.jclouds.domain.Location;
import org.jclouds.io.Payload;
import org.jclouds.io.payloads.ByteSourcePayload;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.blobstore.functions.ToBlobMetadata;
import org.jclouds.openstack.swift.v1.blobstore.functions.ToListContainerOptions;
import org.jclouds.openstack.swift.v1.blobstore.functions.ToResourceMetadata;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.features.ObjectApi;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteSource;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.assistedinject.Assisted;
public class RegionScopedSwiftBlobStore implements BlobStore {
@Inject
protected RegionScopedSwiftBlobStore(Injector baseGraph, BlobStoreContext context, SwiftApi api,
@Memoized Supplier<Set<? extends Location>> locations, @Assisted String regionId) {
checkNotNull(regionId, "regionId");
Optional<? extends Location> found = tryFind(locations.get(), idEquals(regionId));
checkArgument(found.isPresent(), "region %s not in %s", regionId, locations.get());
this.region = found.get();
this.regionId = regionId;
this.toResourceMetadata = new ToResourceMetadata(found.get());
this.context = context;
this.api = api;
// until we parameterize ClearListStrategy with a factory
this.clearList = baseGraph.createChildInjector(new AbstractModule() {
@Override
protected void configure() {
bind(BlobStore.class).toInstance(RegionScopedSwiftBlobStore.this);
}
}).getInstance(ClearListStrategy.class);
}
private final BlobStoreContext context;
private final ClearListStrategy clearList;
private final SwiftApi api;
private final Location region;
private final String regionId;
private final BlobToHttpGetOptions toGetOptions = new BlobToHttpGetOptions();
private final ToListContainerOptions toListContainerOptions = new ToListContainerOptions();
private final ToResourceMetadata toResourceMetadata;
@Override
public Set<? extends Location> listAssignableLocations() {
return ImmutableSet.of(region);
}
@Override
public PageSet<? extends StorageMetadata> list() {
// TODO: there may eventually be >10k containers..
FluentIterable<StorageMetadata> containers = api.getContainerApi(regionId).list()
.transform(toResourceMetadata);
return new PageSetImpl<StorageMetadata>(containers, null);
}
@Override
public boolean containerExists(String container) {
Container val = api.getContainerApi(regionId).get(container);
containerCache.put(container, Optional.fromNullable(val));
return val != null;
}
@Override
public boolean createContainerInLocation(Location location, String container) {
return createContainerInLocation(location, container, CreateContainerOptions.NONE);
}
@Override
public boolean createContainerInLocation(Location location, String container, CreateContainerOptions options) {
checkArgument(location == null || location.equals(region), "location must be null or %s", region);
if (options.isPublicRead()) {
return api.getContainerApi(regionId).create(container, ANYBODY_READ);
}
return api.getContainerApi(regionId).create(container, BASIC_CONTAINER);
}
private static final org.jclouds.openstack.swift.v1.options.CreateContainerOptions BASIC_CONTAINER = new org.jclouds.openstack.swift.v1.options.CreateContainerOptions();
private static final org.jclouds.openstack.swift.v1.options.CreateContainerOptions ANYBODY_READ = new org.jclouds.openstack.swift.v1.options.CreateContainerOptions()
.anybodyRead();
@Override
public PageSet<? extends StorageMetadata> list(String container) {
return list(container, ListContainerOptions.NONE);
}
@Override
public PageSet<? extends StorageMetadata> list(final String container, ListContainerOptions options) {
ObjectApi objectApi = api.getObjectApi(regionId, container);
ObjectList objects = objectApi.list(toListContainerOptions.apply(options));
if (objects == null) {
containerCache.put(container, Optional.<Container> absent());
return new PageSetImpl<StorageMetadata>(ImmutableList.<StorageMetadata> of(), null);
} else {
containerCache.put(container, Optional.of(objects.getContainer()));
List<? extends StorageMetadata> list = transform(objects, toBlobMetadata(container));
int limit = Optional.fromNullable(options.getMaxResults()).or(10000);
String marker = list.size() == limit ? list.get(limit - 1).getName() : null;
// TODO: we should probably deprecate this option
if (options.isDetailed()) {
list = transform(list, new Function<StorageMetadata, StorageMetadata>() {
@Override
public StorageMetadata apply(StorageMetadata input) {
if (input.getType() != StorageType.BLOB) {
return input;
}
return blobMetadata(container, input.getName());
}
});
}
return new PageSetImpl<StorageMetadata>(list, marker);
}
}
@Override
public boolean blobExists(String container, String name) {
return blobMetadata(container, name) != null;
}
@Override
public String putBlob(String container, Blob blob) {
return putBlob(container, blob, PutOptions.NONE);
}
@Override
public String putBlob(String container, Blob blob, PutOptions options) {
if (options.isMultipart()) {
throw new UnsupportedOperationException();
}
ObjectApi objectApi = api.getObjectApi(regionId, container);
return objectApi.put(blob.getMetadata().getName(), blob.getPayload(), metadata(blob.getMetadata().getUserMetadata()));
}
@Override
public BlobMetadata blobMetadata(String container, String name) {
SwiftObject object = api.getObjectApi(regionId, container).get(name);
if (object == null) {
return null;
}
return toBlobMetadata(container).apply(object);
}
@Override
public Blob getBlob(String container, String key) {
return getBlob(container, key, GetOptions.NONE);
}
@Override
public Blob getBlob(String container, String name, GetOptions options) {
ObjectApi objectApi = api.getObjectApi(regionId, container);
SwiftObject object = objectApi.get(name, toGetOptions.apply(options));
if (object == null) {
return null;
}
Blob blob = new BlobImpl(toBlobMetadata(container).apply(object));
blob.setPayload(object.getPayload());
blob.setAllHeaders(object.getHeaders());
return blob;
}
@Override
public void removeBlob(String container, String name) {
api.getObjectApi(regionId, container).delete(name);
}
@Override
public BlobStoreContext getContext() {
return context;
}
@Override
public BlobBuilder blobBuilder(String name) {
return new BlobBuilderImpl().name(name);
}
@Override
public boolean directoryExists(String containerName, String directory) {
return api.getObjectApi(regionId, containerName)
.get(directory) != null;
}
@Override
public void createDirectory(String containerName, String directory) {
api.getObjectApi(regionId, containerName)
.put(directory, directoryPayload);
}
private final Payload directoryPayload = new ByteSourcePayload(ByteSource.wrap(new byte[] {})) {
{
getContentMetadata().setContentType("application/directory");
}
};
@Override
public void deleteDirectory(String containerName, String directory) {
api.getObjectApi(regionId, containerName).delete(directory);
}
@Override
public long countBlobs(String containerName) {
Container container = api.getContainerApi(regionId).get(containerName);
// undefined if container doesn't exist, so default to zero
return container != null ? container.getObjectCount() : 0;
}
@Override
public void clearContainer(String containerName) {
clearContainer(containerName, recursive());
}
@Override
public void clearContainer(String containerName, ListContainerOptions options) {
// this could be implemented to use bulk delete
clearList.execute(containerName, options);
}
@Override
public void deleteContainer(String container) {
clearContainer(container, recursive());
api.getContainerApi(regionId).deleteIfEmpty(container);
containerCache.invalidate(container);
}
@Override
public boolean deleteContainerIfEmpty(String container) {
boolean deleted = api.getContainerApi(regionId).deleteIfEmpty(container);
if (deleted) {
containerCache.invalidate(container);
}
return deleted;
}
protected final LoadingCache<String, Optional<Container>> containerCache = CacheBuilder.newBuilder().build(
new CacheLoader<String, Optional<Container>>() {
public Optional<Container> load(String container) {
return Optional.fromNullable(api.getContainerApi(regionId).get(container));
}
});
protected Function<SwiftObject, MutableBlobMetadata> toBlobMetadata(String container) {
return new ToBlobMetadata(containerCache.getUnchecked(container).get());
}
@Override
public long countBlobs(String containerName, ListContainerOptions options) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.openstack.swift.v1.blobstore;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.inject.Provider;
import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.Uris;
import org.jclouds.http.options.GetOptions;
import org.jclouds.location.Region;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.TemporaryUrlSigner;
import com.google.common.base.Supplier;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
/**
* Uses {@link TemporaryUrlSigner} to sign requests for access to blobs. If no
* interval is supplied, it defaults to a year.
*/
public class RegionScopedTemporaryUrlBlobSigner implements BlobRequestSigner {
@Inject
protected RegionScopedTemporaryUrlBlobSigner(@Region Supplier<Map<String, Supplier<URI>>> regionToUris,
@Named(PROPERTY_SESSION_INTERVAL) long seconds, @TimeStamp Provider<Long> timestamp, SwiftApi api,
@Assisted String regionId) {
checkNotNull(regionId, "regionId");
this.timestamp = timestamp;
this.signer = TemporaryUrlSigner.checkApiEvery(api.getAccountApi(regionId), seconds);
this.storageUrl = regionToUris.get().get(regionId).get();
}
private static final long YEAR = TimeUnit.DAYS.toSeconds(365);
private final BlobToHttpGetOptions toGetOptions = new BlobToHttpGetOptions();
private final Provider<Long> timestamp;
private final TemporaryUrlSigner signer;
private final URI storageUrl;
@Override
public HttpRequest signGetBlob(String container, String name) {
return signGetBlob(container, name, YEAR);
}
@Override
public HttpRequest signGetBlob(String container, String name, long timeInSeconds) {
return sign("GET", container, name, GetOptions.NONE, timestamp.get() + timeInSeconds);
}
@Override
public HttpRequest signGetBlob(String container, String name, org.jclouds.blobstore.options.GetOptions options) {
return sign("GET", container, name, toGetOptions.apply(options), timestamp.get() + YEAR);
}
@Override
public HttpRequest signPutBlob(String container, Blob blob) {
return signPutBlob(container, blob, YEAR);
}
@Override
public HttpRequest signPutBlob(String container, Blob blob, long timeInSeconds) {
return sign("PUT", container, blob.getMetadata().getName(), GetOptions.NONE, timestamp.get() + timeInSeconds);
}
@Override
public HttpRequest signRemoveBlob(String container, String name) {
return sign("DELETE", container, name, GetOptions.NONE, timestamp.get() + YEAR);
}
private HttpRequest sign(String method, String container, String name, GetOptions options, long expires) {
checkNotNull(container, "container");
checkNotNull(name, "name");
URI url = Uris.uriBuilder(storageUrl).appendPath(container).appendPath(name).build();
String signature = signer.sign(method, url.getPath(), expires);
return HttpRequest.builder()
.method(method)
.endpoint(url)
.addQueryParams(options.buildQueryParameters())
.addQueryParam("temp_url_sig", signature)
.addQueryParam("temp_url_expires", String.valueOf(expires))
.headers(options.buildRequestHeaders()).build();
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.openstack.swift.v1.blobstore.config;
import javax.inject.Inject;
import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.date.TimeStamp;
import org.jclouds.openstack.swift.v1.blobstore.RegionScopedTemporaryUrlBlobSigner;
import com.google.common.base.Function;
import com.google.common.collect.ForwardingObject;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class SignUsingTemporaryUrls extends AbstractModule {
@Override
protected void configure() {
install(new FactoryModuleBuilder().build(Factory.class));
}
interface Factory {
RegionScopedTemporaryUrlBlobSigner create(String in);
}
@Provides
Function<String, BlobRequestSigner> blobRequestSigner(FactoryFunction in) {
return in;
}
static class FactoryFunction extends ForwardingObject implements Function<String, BlobRequestSigner> {
@Inject
Factory delegate;
@Override
protected Factory delegate() {
return delegate;
}
@Override
public BlobRequestSigner apply(String in) {
return delegate.create(in);
}
}
@Provides
@TimeStamp
protected Long unixEpochTimestamp() {
return System.currentTimeMillis() / 1000;
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.openstack.swift.v1.blobstore.config;
import javax.inject.Inject;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.attr.ConsistencyModel;
import org.jclouds.openstack.swift.v1.blobstore.RegionScopedBlobStoreContext;
import org.jclouds.openstack.swift.v1.blobstore.RegionScopedSwiftBlobStore;
import com.google.common.base.Function;
import com.google.common.collect.ForwardingObject;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class SwiftBlobStoreContextModule extends AbstractModule {
@Override
protected void configure() {
bind(ConsistencyModel.class).toInstance(ConsistencyModel.EVENTUAL);
bind(BlobStoreContext.class).to(RegionScopedBlobStoreContext.class);
install(new FactoryModuleBuilder().build(Factory.class));
}
interface Factory {
RegionScopedSwiftBlobStore create(String in);
}
@Provides
Function<String, BlobStore> blobStore(FactoryFunction in) {
return in;
}
static class FactoryFunction extends ForwardingObject implements Function<String, BlobStore> {
@Inject
Factory delegate;
@Override
protected Factory delegate() {
return delegate;
}
@Override
public BlobStore apply(String in) {
return delegate.create(in);
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.openstack.swift.v1.blobstore.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
import org.jclouds.blobstore.strategy.IfDirectoryReturnNameStrategy;
import org.jclouds.blobstore.strategy.internal.MarkersIfDirectoryReturnNameStrategy;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import com.google.common.base.Function;
public class ToBlobMetadata implements Function<SwiftObject, MutableBlobMetadata> {
private final IfDirectoryReturnNameStrategy ifDirectoryReturnName = new MarkersIfDirectoryReturnNameStrategy();
private final Container container;
public ToBlobMetadata(Container container) {
this.container = checkNotNull(container, "container");
}
@Override
public MutableBlobMetadata apply(SwiftObject from) {
if (from == null)
return null;
MutableBlobMetadata to = new MutableBlobMetadataImpl();
to.setContainer(container.getName());
if (container.getAnybodyRead().isPresent()) {
to.setPublicUri(from.getUri());
}
to.setUri(from.getUri());
to.setETag(from.getETag());
to.setName(from.getName());
to.setLastModified(from.getLastModified());
to.setContentMetadata(from.getPayload().getContentMetadata());
to.getContentMetadata().setContentMD5(from.getPayload().getContentMetadata().getContentMD5AsHashCode());
to.getContentMetadata().setExpires(from.getPayload().getContentMetadata().getExpires());
to.setUserMetadata(from.getMetadata());
String directoryName = ifDirectoryReturnName.execute(to);
if (directoryName != null) {
to.setName(directoryName);
to.setType(StorageType.RELATIVE_PATH);
} else {
to.setType(StorageType.BLOB);
}
return to;
}
@Override
public String toString() {
return "ObjectToBlobMetadata(" + container + ")";
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.openstack.swift.v1.blobstore.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.blobstore.options.ListContainerOptions;
import com.google.common.base.Function;
public class ToListContainerOptions implements
Function<ListContainerOptions, org.jclouds.openstack.swift.v1.options.ListContainerOptions> {
@Override
public org.jclouds.openstack.swift.v1.options.ListContainerOptions apply(ListContainerOptions from) {
checkNotNull(from, "set options to instance NONE instead of passing null");
org.jclouds.openstack.swift.v1.options.ListContainerOptions options = new org.jclouds.openstack.swift.v1.options.ListContainerOptions();
if ((from.getDir() == null) && (from.isRecursive())) {
options.prefix("");
}
if ((from.getDir() == null) && (!from.isRecursive())) {
options.path("");
}
if ((from.getDir() != null) && (from.isRecursive())) {
options.prefix(from.getDir().endsWith("/") ? from.getDir() : from.getDir() + "/");
}
if ((from.getDir() != null) && (!from.isRecursive())) {
options.path(from.getDir());
}
if (from.getMarker() != null) {
options.marker(from.getMarker());
}
if (from.getMaxResults() != null) {
options.limit(from.getMaxResults());
}
return options;
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.openstack.swift.v1.blobstore.functions;
import org.jclouds.blobstore.domain.MutableStorageMetadata;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.domain.internal.MutableStorageMetadataImpl;
import org.jclouds.domain.Location;
import org.jclouds.openstack.swift.v1.domain.Container;
import com.google.common.base.Function;
public class ToResourceMetadata implements Function<Container, StorageMetadata> {
private Location region;
public ToResourceMetadata(Location region) {
this.region = region;
}
@Override
public StorageMetadata apply(Container from) {
MutableStorageMetadata to = new MutableStorageMetadataImpl();
to.setName(from.getName());
to.setLocation(region);
to.setType(StorageType.CONTAINER);
to.setUserMetadata(from.getMetadata());
return to;
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.openstack.swift.v1.config;
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.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.handlers.SwiftErrorHandler;
import org.jclouds.rest.ConfiguresHttpApi;
import org.jclouds.rest.config.HttpApiModule;
@ConfiguresHttpApi
public abstract class BaseSwiftHttpApiModule<A extends SwiftApi> extends HttpApiModule<A> {
protected BaseSwiftHttpApiModule(Class<A> api) {
super(api);
}
@Override
protected void configure() {
super.configure();
}
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(SwiftErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(SwiftErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(SwiftErrorHandler.class);
}
}

View File

@ -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.openstack.swift.v1.config;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.rest.ConfiguresHttpApi;
@ConfiguresHttpApi
public class SwiftHttpApiModule extends BaseSwiftHttpApiModule<SwiftApi> {
public SwiftHttpApiModule() {
super(SwiftApi.class);
}
@Override
protected void configure() {
super.configure();
}
}

View File

@ -0,0 +1,119 @@
/*
* 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.openstack.swift.v1.config;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Map;
import org.jclouds.json.config.GsonModule.DateAdapter;
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
public class SwiftTypeAdapters extends AbstractModule {
@Override
protected void configure() {
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
}
@Provides
public Map<Type, Object> provideCustomAdapterBindings() {
return ImmutableMap.<Type, Object> builder()
.put(ExtractArchiveResponse.class, new ExtractArchiveResponseAdapter())
.put(BulkDeleteResponse.class, new BulkDeleteResponseAdapter()).build();
}
static class ExtractArchiveResponseAdapter extends TypeAdapter<ExtractArchiveResponse> {
@Override
public ExtractArchiveResponse read(JsonReader reader) throws IOException {
int created = 0;
Builder<String, String> errors = ImmutableMap.<String, String> builder();
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
if (key.equals("Number Files Created")) {
created = reader.nextInt();
} else if (key.equals("Errors")) {
readErrors(reader, errors);
} else {
reader.skipValue();
}
}
reader.endObject();
return ExtractArchiveResponse.create(created, errors.build());
}
@Override
public void write(JsonWriter arg0, ExtractArchiveResponse arg1) throws IOException {
throw new UnsupportedOperationException();
}
}
static class BulkDeleteResponseAdapter extends TypeAdapter<BulkDeleteResponse> {
@Override
public BulkDeleteResponse read(JsonReader reader) throws IOException {
int deleted = 0;
int notFound = 0;
Builder<String, String> errors = ImmutableMap.<String, String> builder();
reader.beginObject();
while (reader.hasNext()) {
String key = reader.nextName();
if (key.equals("Number Deleted")) {
deleted = reader.nextInt();
} else if (key.equals("Number Not Found")) {
notFound = reader.nextInt();
} else if (key.equals("Errors")) {
readErrors(reader, errors);
} else {
reader.skipValue();
}
}
reader.endObject();
return BulkDeleteResponse.create(deleted, notFound, errors.build());
}
@Override
public void write(JsonWriter arg0, BulkDeleteResponse arg1) throws IOException {
throw new UnsupportedOperationException();
}
}
static void readErrors(JsonReader reader, Builder<String, String> errors) throws IOException {
reader.beginArray();
while (reader.hasNext()) {
reader.beginArray();
String decodedPath = URI.create(reader.nextString()).getPath();
errors.put(decodedPath, reader.nextString());
reader.endArray();
}
reader.endArray();
}
}

View File

@ -0,0 +1,219 @@
/*
* 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.openstack.swift.v1.domain;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
/**
* Represents an Account in OpenStack Object Storage.
*
* @see org.jclouds.openstack.swift.v1.features.AccountApi
*/
public class Account {
private final long containerCount;
private final long objectCount;
private final long bytesUsed;
private final Map<String, String> metadata;
private final Multimap<String, String> headers;
// parsed from headers, so ConstructorProperties here would be misleading
protected Account(long containerCount, long objectCount, long bytesUsed, Map<String, String> metadata,
Multimap<String, String> headers) {
this.containerCount = containerCount;
this.objectCount = objectCount;
this.bytesUsed = bytesUsed;
this.metadata = metadata == null ? ImmutableMap.<String, String> of() : metadata;
this.headers = headers == null ? ImmutableMultimap.<String, String> of() : headers;
}
/**
* @return The count of containers for this account.
*/
public long getContainerCount() {
return containerCount;
}
/**
* @return The count of objects for this account.
*/
public long getObjectCount() {
return objectCount;
}
/**
* @return The number of bytes used by this account.
*/
public long getBytesUsed() {
return bytesUsed;
}
/**
* @return The {@link Optional&lt;String&gt;} temporary URL key for this account.
*/
public Optional<String> getTemporaryUrlKey() {
return Optional.fromNullable(metadata.get("temp-url-key"));
}
/**
* <h3>NOTE</h3>
* In current swift implementations, headers keys are lower-cased. This means
* characters such as turkish will probably not work out well.
*
* @return a {@code Map<String, String>} containing the account metadata.
*/
public Map<String, String> getMetadata() {
return metadata;
}
/**
* @return The HTTP headers for this account.
*/
public Multimap<String, String> getHeaders() {
return headers;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Account) {
Account that = Account.class.cast(object);
return equal(getContainerCount(), that.getContainerCount())
&& equal(getObjectCount(), that.getObjectCount())
&& equal(getBytesUsed(), that.getBytesUsed())
&& equal(getMetadata(), that.getMetadata());
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getContainerCount(), getObjectCount(), getBytesUsed(), getMetadata());
}
@Override
public String toString() {
return string().toString();
}
protected ToStringHelper string() {
return toStringHelper(this)
.add("containerCount", getContainerCount())
.add("objectCount", getObjectCount())
.add("bytesUsed", getBytesUsed())
.add("metadata", getMetadata());
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromAccount(this);
}
public static class Builder {
protected long containerCount;
protected long objectCount;
protected long bytesUsed;
protected Multimap<String, String> headers = ImmutableMultimap.of();
protected Map<String, String> metadata = ImmutableMap.of();
/**
* @param containerCount the count of containers for this account.
*
* @see Account#getContainerCount()
*/
public Builder containerCount(long containerCount) {
this.containerCount = containerCount;
return this;
}
/**
* @param objectCount the count of objects for this account.
*
* @see Account#getObjectCount()
*/
public Builder objectCount(long objectCount) {
this.objectCount = objectCount;
return this;
}
/**
* @param bytesUsed the number of bytes used by this account.
*
* @see Account#getBytesUsed()
*/
public Builder bytesUsed(long bytesUsed) {
this.bytesUsed = bytesUsed;
return this;
}
/**
* <h3>NOTE</h3>
* This method will lower-case all metadata keys due to a Swift implementation
* decision.
*
* @param metadata the metadata for this account.
*
* @see Account#getMetadata()
*/
public Builder metadata(Map<String, String> metadata) {
ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String> builder();
for (Entry<String, String> entry : checkNotNull(metadata, "metadata").entrySet()) {
builder.put(entry.getKey().toLowerCase(), entry.getValue());
}
this.metadata = builder.build();
return this;
}
/**
* @see Account#getHeaders()
*/
public Builder headers(Multimap<String, String> headers) {
this.headers = headers;
return this;
}
public Account build() {
return new Account(containerCount, objectCount, bytesUsed, metadata, headers);
}
public Builder fromAccount(Account from) {
return containerCount(from.getContainerCount())
.objectCount(from.getObjectCount())
.bytesUsed(from.getBytesUsed())
.metadata(from.getMetadata())
.headers(from.getHeaders());
}
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.openstack.swift.v1.domain;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;
/**
* Represents a response from a Bulk Delete request.
*
* @see org.jclouds.openstack.swift.v1.features.BulkApi
*/
public class BulkDeleteResponse {
public static BulkDeleteResponse create(int deleted, int notFound, Map<String, String> errors) {
return new BulkDeleteResponse(deleted, notFound, errors);
}
private final int deleted;
private final int notFound;
private final Map<String, String> errors;
private BulkDeleteResponse(int deleted, int notFound, Map<String, String> errors) {
this.deleted = deleted;
this.notFound = notFound;
this.errors = checkNotNull(errors, "errors");
}
/**
* @return The number of files deleted.
* */
public int getDeleted() {
return deleted;
}
/**
* @return The number of files not found.
*/
public int getNotFound() {
return notFound;
}
/**
* @return a {@code Map<String, String>} containing each path that failed
* to be deleted and its corresponding error response.
*/
public Map<String, String> getErrors() {
return errors;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof BulkDeleteResponse) {
BulkDeleteResponse that = BulkDeleteResponse.class.cast(object);
return equal(getDeleted(), that.getDeleted())
&& equal(getNotFound(), that.getNotFound())
&& equal(getErrors(), that.getErrors());
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getDeleted(), getNotFound(), getErrors());
}
protected ToStringHelper string() {
return toStringHelper(this)
.add("deleted", getDeleted())
.add("notFound", getNotFound())
.add("errors", getErrors());
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,237 @@
/*
* 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.openstack.swift.v1.domain;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
/**
* Represents a Container in OpenStack Object Storage.
*
* @see org.jclouds.openstack.swift.v1.features.ContainerApi
*/
public class Container implements Comparable<Container> {
private final String name;
private final long objectCount;
private final long bytesUsed;
private final Optional<Boolean> anybodyRead;
private final Map<String, String> metadata;
private final Multimap<String, String> headers;
@ConstructorProperties({ "name", "count", "bytes", "anybodyRead", "metadata", "headers"})
protected Container(String name, long objectCount, long bytesUsed, Optional<Boolean> anybodyRead,
Map<String, String> metadata, Multimap<String, String> headers) {
this.name = checkNotNull(name, "name");
this.objectCount = objectCount;
this.bytesUsed = bytesUsed;
this.anybodyRead = anybodyRead == null ? Optional.<Boolean> absent() : anybodyRead;
this.metadata = metadata == null ? ImmutableMap.<String, String> of() : metadata;
this.headers = headers == null ? ImmutableMultimap.<String, String> of() : headers;
}
/**
* @return The name of this container.
*/
public String getName() {
return name;
}
/**
* @return The count of objects for this container.
*/
public long getObjectCount() {
return objectCount;
}
/**
* @return The number of bytes used by this container.
*/
public long getBytesUsed() {
return bytesUsed;
}
/**
* Absent except in {@link ContainerApi#get(String) Get Container} commands.
*
* @return true if this container is publicly readable, false otherwise.
*
* @see org.jclouds.openstack.swift.v1.options.CreateContainerOptions#anybodyRead()
*/
public Optional<Boolean> getAnybodyRead() {
return anybodyRead;
}
/**
* <h3>NOTE</h3>
* In current swift implementations, headers keys are lower-cased. This means
* characters such as turkish will probably not work out well.
*
* @return a {@code Map<String, String>} containing this container's metadata.
*/
public Map<String, String> getMetadata() {
return metadata;
}
/**
* @return The HTTP headers for this account.
*/
public Multimap<String, String> getHeaders() {
return headers;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Container) {
final Container that = Container.class.cast(object);
return equal(getName(), that.getName())
&& equal(getObjectCount(), that.getObjectCount())
&& equal(getBytesUsed(), that.getBytesUsed())
&& equal(getMetadata(), that.getMetadata());
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getName(), getObjectCount(), getBytesUsed(), getAnybodyRead(), getMetadata());
}
@Override
public String toString() {
return string().toString();
}
protected ToStringHelper string() {
return toStringHelper(this).omitNullValues()
.add("name", getName())
.add("objectCount", getObjectCount())
.add("bytesUsed", getBytesUsed())
.add("anybodyRead", getAnybodyRead().orNull())
.add("metadata", getMetadata());
}
@Override
public int compareTo(Container that) {
if (that == null)
return 1;
if (this == that)
return 0;
return this.getName().compareTo(that.getName());
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromContainer(this);
}
public static class Builder {
protected String name;
protected long objectCount;
protected long bytesUsed;
protected Optional<Boolean> anybodyRead = Optional.absent();
protected Map<String, String> metadata = ImmutableMap.of();
protected Multimap<String, String> headers = ImmutableMultimap.of();
/**
* @see Container#getName()
*/
public Builder name(String name) {
this.name = checkNotNull(name, "name");
return this;
}
/**
* @see Container#getObjectCount()
*/
public Builder objectCount(long objectCount) {
this.objectCount = objectCount;
return this;
}
/**
* @see Container#getBytesUsed()
*/
public Builder bytesUsed(long bytesUsed) {
this.bytesUsed = bytesUsed;
return this;
}
/**
* @see Container#getAnybodyRead()
*/
public Builder anybodyRead(Boolean anybodyRead) {
this.anybodyRead = Optional.fromNullable(anybodyRead);
return this;
}
/**
* <h3>NOTE</h3>
* This method will lower-case all metadata keys.
*
* @see Container#getMetadata()
*/
public Builder metadata(Map<String, String> metadata) {
ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String> builder();
for (Entry<String, String> entry : checkNotNull(metadata, "metadata").entrySet()) {
builder.put(entry.getKey().toLowerCase(), entry.getValue());
}
this.metadata = builder.build();
return this;
}
/**
* @see Container#getHeaders()
*/
public Builder headers(Multimap<String, String> headers) {
this.headers = headers;
return this;
}
public Container build() {
return new Container(name, objectCount, bytesUsed, anybodyRead, metadata, headers);
}
public Builder fromContainer(Container from) {
return name(from.getName())
.objectCount(from.getObjectCount())
.bytesUsed(from.getBytesUsed())
.anybodyRead(from.getAnybodyRead().orNull())
.metadata(from.getMetadata())
.headers(from.getHeaders());
}
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.openstack.swift.v1.domain;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;
/**
* Represents a response from an Extract Archive request.
*
* @see org.jclouds.openstack.swift.v1.features.BulkApi
*/
public class ExtractArchiveResponse {
public static ExtractArchiveResponse create(int created, Map<String, String> errors) {
return new ExtractArchiveResponse(created, errors);
}
private final int created;
private final Map<String, String> errors;
private ExtractArchiveResponse(int created, Map<String, String> errors) {
this.created = created;
this.errors = checkNotNull(errors, "errors");
}
/**
* @return The number of files created.
*/
public int getCreated() {
return created;
}
/**
* @return a {@code Map<String, String>} containing each path that failed
* to be created and its corresponding error response.
*/
public Map<String, String> getErrors() {
return errors;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof ExtractArchiveResponse) {
ExtractArchiveResponse that = ExtractArchiveResponse.class.cast(object);
return equal(getCreated(), that.getCreated())
&& equal(getErrors(), that.getErrors());
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getCreated(), getErrors());
}
protected ToStringHelper string() {
return toStringHelper(this)
.add("created", getCreated())
.add("errors", getErrors());
}
@Override
public String toString() {
return string().toString();
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.openstack.swift.v1.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import com.google.common.collect.ForwardingList;
/**
* Represents a list of objects in a container.
*
* @see Container
* @see SwiftObject
* @see org.jclouds.openstack.swift.v1.features.ObjectApi#list()
*/
public class ObjectList extends ForwardingList<SwiftObject> {
public static ObjectList create(List<SwiftObject> objects, Container container) {
return new ObjectList(objects, container);
}
private final List<SwiftObject> objects;
private final Container container;
protected ObjectList(List<SwiftObject> objects, Container container) {
this.objects = checkNotNull(objects, "objects");
this.container = checkNotNull(container, "container");
}
/**
* @return the parent {@link Container} the objects reside in.
*/
public Container getContainer() {
return container;
}
@Override
protected List<SwiftObject> delegate() {
return objects;
}
}

View File

@ -0,0 +1,136 @@
/*
* 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.openstack.swift.v1.domain;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Named;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;
/**
* Represents a single segment of a multi-part upload.
*
* @see org.jclouds.openstack.swift.v1.features.StaticLargeObjectApi
*/
public class Segment {
private final String path;
private final String etag;
@Named("size_bytes")
private final long sizeBytes;
private Segment(String path, String etag, long sizeBytes) {
this.path = checkNotNull(path, "path");
this.etag = checkNotNull(etag, "etag of %s", path);
this.sizeBytes = checkNotNull(sizeBytes, "sizeBytes of %s", path);
}
/**
* @return The container and object name in the format: {@code <container-name>/<object-name>}
*/
public String getPath() {
return path;
}
/**
* @return The ETag of the content of the segment object.
*/
public String getETag() {
return etag;
}
/**
* @return The size of the segment object.
*/
public long getSizeBytes() {
return sizeBytes;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Segment) {
Segment that = Segment.class.cast(object);
return equal(getPath(), that.getPath())
&& equal(getETag(), that.getETag())
&& equal(getSizeBytes(), that.getSizeBytes());
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getPath(), getETag(), getSizeBytes());
}
@Override
public String toString() {
return string().toString();
}
protected ToStringHelper string() {
return toStringHelper(this)
.add("path", getPath())
.add("etag", getETag())
.add("sizeBytes", getSizeBytes());
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
protected String path;
protected String etag;
protected long sizeBytes;
/**
* @see Segment#getPath()
*/
public Builder path(String path) {
this.path = path;
return this;
}
/**
* @see Segment#getEtag()
*/
public Builder etag(String etag) {
this.etag = etag;
return this;
}
/**
* @see Segment#getSizeBytes()
*/
public Builder sizeBytes(long sizeBytes) {
this.sizeBytes = sizeBytes;
return this;
}
public Segment build() {
return new Segment(path, etag, sizeBytes);
}
}
}

View File

@ -0,0 +1,267 @@
/*
* 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.openstack.swift.v1.domain;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.io.Payload;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
/**
* Represents an object in OpenStack Object Storage.
*
*
* @see ObjectApi
*/
public class SwiftObject implements Comparable<SwiftObject> {
private final String name;
private final URI uri;
private final String etag;
private final Date lastModified;
private final Multimap<String, String> headers;
private final Map<String, String> metadata;
private final Payload payload;
protected SwiftObject(String name, URI uri, String etag, Date lastModified,
Multimap<String, String> headers, Map<String, String> metadata, Payload payload) {
this.name = checkNotNull(name, "name");
this.uri = checkNotNull(uri, "uri of %s", uri);
this.etag = checkNotNull(etag, "etag of %s", name).replace("\"", "");
this.lastModified = checkNotNull(lastModified, "lastModified of %s", name);
this.headers = headers == null ? ImmutableMultimap.<String, String> of() : checkNotNull(headers, "headers of %s", name);
this.metadata = metadata == null ? ImmutableMap.<String, String> of() : metadata;
this.payload = checkNotNull(payload, "payload of %s", name);
}
/**
* @return The name of this object.
*/
public String getName() {
return name;
}
/**
* @return The {@link URI} for this object.
*/
public URI getUri() {
return uri;
}
/**
* @return The ETag of the content of this object.
* @deprecated Please use {@link #getETag()} as this method will be removed in jclouds 1.8.
*/
public String getEtag() {
return etag;
}
/**
* @return The ETag of the content of this object.
*/
public String getETag() {
return etag;
}
/**
* @return The {@link Date} that this object was last modified.
*/
public Date getLastModified() {
return lastModified;
}
/**
* @return The HTTP headers for this object.
*/
public Multimap<String, String> getHeaders() {
return headers;
}
/**
* <h3>NOTE</h3>
* In current swift implementations, headers keys are lower-cased. This means
* characters such as turkish will probably not work out well.
*
* @return a {@code Map<String, String>} containing this object's metadata. The map is empty
* except in {@link ObjectApi#head(String) GetObjectMetadata} or
* {@link ObjectApi#get(String) GetObject} commands.
*/
public Map<String, String> getMetadata() {
return metadata;
}
/**
* <h3>NOTE</h3>
* The object will only have a {@link Payload#getInput()} when retrieved via the
* {@link ObjectApi#get(String) GetObject} command.
*
* @return The {@link Payload} for this object.
*/
public Payload getPayload() {
return payload;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof SwiftObject) {
final SwiftObject that = SwiftObject.class.cast(object);
return equal(getName(), that.getName())
&& equal(getUri(), that.getUri())
&& equal(getETag(), that.getETag());
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hashCode(getName(), getUri(), getETag());
}
@Override
public String toString() {
return string().toString();
}
protected ToStringHelper string() {
return toStringHelper(this)
.add("name", getName())
.add("uri", getUri())
.add("etag", getETag())
.add("lastModified", getLastModified())
.add("metadata", getMetadata());
}
@Override
public int compareTo(SwiftObject that) {
if (that == null)
return 1;
if (this == that)
return 0;
return this.getName().compareTo(that.getName());
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromObject(this);
}
public static class Builder {
protected String name;
protected URI uri;
protected String etag;
protected Date lastModified;
protected Payload payload;
protected Multimap<String, String> headers = ImmutableMultimap.of();
protected Map<String, String> metadata = ImmutableMap.of();
/**
* @see SwiftObject#getName()
*/
public Builder name(String name) {
this.name = checkNotNull(name, "name");
return this;
}
/**
* @see SwiftObject#getUri()
*/
public Builder uri(URI uri) {
this.uri = checkNotNull(uri, "uri");
return this;
}
/**
* @see SwiftObject#getETag()
*/
public Builder etag(String etag) {
this.etag = etag;
return this;
}
/**
* @see SwiftObject#getLastModified()
*/
public Builder lastModified(Date lastModified) {
this.lastModified = lastModified;
return this;
}
/**
* @see SwiftObject#getPayload()
*/
public Builder payload(Payload payload) {
this.payload = payload;
return this;
}
/**
* @see SwiftObject#getHeaders()
*/
public Builder headers(Multimap<String, String> headers) {
this.headers = headers;
return this;
}
/**
* Will lower-case all metadata keys due to a swift implementation
* decision.
*
* @see SwiftObject#getMetadata()
*/
public Builder metadata(Map<String, String> metadata) {
ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String> builder();
for (Entry<String, String> entry : checkNotNull(metadata, "metadata").entrySet()) {
builder.put(entry.getKey().toLowerCase(), entry.getValue());
}
this.metadata = builder.build();
return this;
}
public SwiftObject build() {
return new SwiftObject(name, uri, etag, lastModified, headers, metadata, payload);
}
public Builder fromObject(SwiftObject from) {
return name(from.getName())
.uri(from.getUri())
.etag(from.getETag())
.lastModified(from.getLastModified())
.headers(from.getHeaders())
.metadata(from.getMetadata())
.payload(from.getPayload());
}
}
}

View File

@ -0,0 +1,111 @@
/*
* 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.openstack.swift.v1.features;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_TEMPORARY_URL_KEY;
import java.util.Map;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.HEAD;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindAccountMetadataToHeaders;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindRemoveAccountMetadataToHeaders;
import org.jclouds.openstack.swift.v1.domain.Account;
import org.jclouds.openstack.swift.v1.functions.ParseAccountFromHeaders;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import com.google.common.annotations.Beta;
/**
* Provides access to the OpenStack Object Storage (Swift) Account API features.
*
* <p/>
* Account metadata prefixed with {@code X-Account-Meta-} will be converted
* appropriately using a binder/parser.
* <p/>
* This API is new to jclouds and hence is in Beta. That means we need people to use it and give us feedback. Based
* on that feedback, minor changes to the interfaces may happen. This code will replace
* org.jclouds.openstack.swift.SwiftClient in jclouds 2.0 and it is recommended you adopt it sooner than later.
*
*
* @see {@link Account}
*/
@Beta
@RequestFilters(AuthenticateRequest.class)
@Consumes(APPLICATION_JSON)
public interface AccountApi {
/**
* Gets the {@link Account}.
*
* @return The {@link Account} object.
*/
@Named("account:get")
@HEAD
@ResponseParser(ParseAccountFromHeaders.class)
Account get();
/**
* Creates or updates the {@link Account} metadata.
*
* @param metadata the metadata to create or update.
*
* @return {@code true} if the metadata was successfully created or updated,
* {@code false} if not.
*/
@Named("account:updateMetadata")
@POST
@Fallback(FalseOnNotFoundOr404.class)
boolean updateMetadata(@BinderParam(BindAccountMetadataToHeaders.class) Map<String, String> metadata);
/**
* Replaces the temporary URL key for the {@link Account}.
*
* @param temporaryUrlKey the temporary URL key to update.
*
* @return {@code true} if the temporary URL key was successfully updated,
* {@code false} if not.
*/
@Named("account:updateTemporaryUrlKey")
@POST
@Fallback(FalseOnNotFoundOr404.class)
boolean updateTemporaryUrlKey(@HeaderParam(ACCOUNT_TEMPORARY_URL_KEY) String temporaryUrlKey);
/**
* Deletes metadata from the {@link Account}.
*
* @param metadata the metadata to delete.
*
* @return {@code true} if the metadata was successfully deleted,
* {@code false} if not.
*/
@Named("account:deleteMetadata")
@POST
@Fallback(FalseOnNotFoundOr404.class)
boolean deleteMetadata(@BinderParam(BindRemoveAccountMetadataToHeaders.class) Map<String, String> metadata);
}

View File

@ -0,0 +1,105 @@
/*
* 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.openstack.swift.v1.features;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.net.UrlEscapers.urlFragmentEscaper;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import org.jclouds.http.HttpRequest;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.v1.binders.SetPayload;
import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
import org.jclouds.rest.Binder;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters;
import com.google.common.annotations.Beta;
import com.google.common.base.Joiner;
/**
* Provides access to the OpenStack Object Storage (Swift) Bulk API features.
* <p/>
* This API is new to jclouds and hence is in Beta. That means we need people to use it and give us feedback. Based
* on that feedback, minor changes to the interfaces may happen. This code will replace
* org.jclouds.openstack.swift.SwiftClient in jclouds 2.0 and it is recommended you adopt it sooner than later.
*/
@Beta
@RequestFilters(AuthenticateRequest.class)
@Consumes(APPLICATION_JSON)
public interface BulkApi {
/**
* Extracts a tar archive at the path specified as {@code path}.
*
* @param path
* the path to extract under.
* @param payload
* the {@link Payload payload} archive.
* @param format
* one of {@code tar}, {@code tar.gz}, or {@code tar.bz2}
*
* @return {@link BulkDeleteResponse#getErrors()} are empty on success.
*/
@Named("bulk:extractArchive")
@PUT
@Path("/{path}")
ExtractArchiveResponse extractArchive(@PathParam("path") String path,
@BinderParam(SetPayload.class) Payload payload, @QueryParam("extract-archive") String format);
/**
* Deletes multiple objects or containers, if present.
*
* @param paths
* format of {@code container}, for an empty container, or
* {@code container/object} for an object.
*
* @return {@link BulkDeleteResponse#getErrors()} are empty on success.
*/
@Named("bulk:delete")
@DELETE
@QueryParams(keys = "bulk-delete")
BulkDeleteResponse bulkDelete(@BinderParam(UrlEncodeAndJoinOnNewline.class) Iterable<String> paths);
// NOTE: this cannot be tested on MWS and is also brittle, as it relies on
// sending a body on DELETE.
// https://bugs.launchpad.net/swift/+bug/1232787
static class UrlEncodeAndJoinOnNewline implements Binder {
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
String encodedAndNewlineDelimited = Joiner.on('\n').join(
transform(Iterable.class.cast(input), urlFragmentEscaper().asFunction()));
Payload payload = Payloads.newStringPayload(encodedAndNewlineDelimited);
payload.getContentMetadata().setContentType(TEXT_PLAIN);
return (R) request.toBuilder().payload(payload).build();
}
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.openstack.swift.v1.features;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.jclouds.openstack.swift.v1.SwiftFallbacks.TrueOn404FalseOn409;
import java.util.Map;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.Fallbacks.EmptyFluentIterableOnNotFoundOr404;
import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindContainerMetadataToHeaders;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindRemoveContainerMetadataToHeaders;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.openstack.swift.v1.functions.FalseOnAccepted;
import org.jclouds.openstack.swift.v1.functions.ParseContainerFromHeaders;
import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.openstack.swift.v1.options.UpdateContainerOptions;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import com.google.common.annotations.Beta;
import com.google.common.collect.FluentIterable;
/**
* Provides access to the OpenStack Object Storage (Swift) Container API features.
* <p/>
* This API is new to jclouds and hence is in Beta. That means we need people to use it and give us feedback. Based
* on that feedback, minor changes to the interfaces may happen. This code will replace
* {@code org.jclouds.openstack.swift.SwiftClient} in jclouds 2.0 and it is recommended you adopt it sooner than later.
*/
@Beta
@RequestFilters(AuthenticateRequest.class)
@Consumes(APPLICATION_JSON)
public interface ContainerApi {
/**
* Lists up to 10,000 containers.
*
* <h3>NOTE</h3>
* This method returns a list of {@link Container} objects <b>without</b> metadata. To retrieve
* the {@link Container} metadata, use the {@link #get(String)} method.
* <p/>
*
* @return a list of {@link Container containers} ordered by name.
*/
@Named("container:list")
@GET
@Fallback(EmptyFluentIterableOnNotFoundOr404.class)
FluentIterable<Container> list();
/**
* Lists containers with the supplied {@link ListContainerOptions}.
*
* <h3>NOTE</h3>
* This method returns a list of {@link Container} objects <b>without</b> metadata. To retrieve
* the {@link Container} metadata, use the {@link #get(String)} method.
* <p/>
*
* @param options
* the options to control the output list.
*
* @return a list of {@link Container containers} ordered by name.
*/
@Named("container:list")
@GET
@Fallback(EmptyFluentIterableOnNotFoundOr404.class)
FluentIterable<Container> list(ListContainerOptions options);
/**
* Creates a container, if not already present.
*
* @param containerName
* corresponds to {@link Container#getName()}.
*
* @return {@code true} if the container was created, {@code false} if the container already existed.
*/
@Named("container:create")
@PUT
@Path("/{containerName}")
@ResponseParser(FalseOnAccepted.class)
boolean create(@PathParam("containerName") String containerName);
/**
* Creates a container, if not already present.
*
* @param containerName
* corresponds to {@link Container#getName()}.
* @param options
* the options to use when creating the container.
*
* @return {@code true} if the container was created, {@code false} if the container already existed.
*/
@Named("container:create")
@PUT
@Path("/{containerName}")
@ResponseParser(FalseOnAccepted.class)
boolean create(@PathParam("containerName") String containerName, CreateContainerOptions options);
/**
* Gets the {@link Container}.
*
* @param containerName
* corresponds to {@link Container#getName()}.
*
* @return the {@link Container}, or {@code null} if not found.
*/
@Named("container:get")
@HEAD
@Path("/{containerName}")
@ResponseParser(ParseContainerFromHeaders.class)
@Fallback(NullOnNotFoundOr404.class)
@Nullable
Container get(@PathParam("containerName") String containerName);
/**
* Updates the {@link Container}.
*
* @param containerName
* the container name corresponding to {@link Container#getName()}.
* @param options
* the container options to update.
*
* @return {@code true} if the container metadata was successfully created or updated,
* {@code false} if not.
*/
@Named("container:update")
@POST
@Path("/{containerName}")
@Fallback(FalseOnNotFoundOr404.class)
boolean update(@PathParam("containerName") String containerName, UpdateContainerOptions options);
/**
* Creates or updates the {@link Container} metadata.
*
* @param containerName
* the container name corresponding to {@link Container#getName()}.
* @param metadata
* the container metadata to create or update.
*
* @return {@code true} if the container metadata was successfully created or updated,
* {@code false} if not.
*/
@Named("container:updateMetadata")
@POST
@Path("/{containerName}")
@Fallback(FalseOnNotFoundOr404.class)
boolean updateMetadata(@PathParam("containerName") String containerName,
@BinderParam(BindContainerMetadataToHeaders.class) Map<String, String> metadata);
/**
* Deletes {@link Container} metadata.
*
* @param containerName
* corresponds to {@link Container#getName()}.
* @param metadata
* the container metadata to delete.
*
* @return {@code true} if the container metadata was successfully deleted,
* {@code false} if not.
*/
@Named("container:deleteMetadata")
@POST
@Path("/{containerName}")
@Fallback(FalseOnNotFoundOr404.class)
boolean deleteMetadata(@PathParam("containerName") String containerName,
@BinderParam(BindRemoveContainerMetadataToHeaders.class) Map<String, String> metadata);
/**
* Deletes a {@link Container}, if empty.
*
* @param containerName
* corresponds to {@link Container#getName()}.
*
* @return {@code true} if the container was deleted or not present.
*
* @throws IllegalStateException if the container was not empty.
*/
@Named("container:deleteIfEmpty")
@DELETE
@Path("/{containerName}")
@Fallback(TrueOn404FalseOn409.class)
boolean deleteIfEmpty(@PathParam("containerName") String containerName) throws IllegalStateException;
}

View File

@ -0,0 +1,265 @@
/*
* 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.openstack.swift.v1.features;
import static com.google.common.net.HttpHeaders.EXPECT;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_COPY_FROM;
import java.util.Map;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.blobstore.BlobStoreFallbacks.FalseOnContainerNotFound;
import org.jclouds.http.options.GetOptions;
import org.jclouds.io.Payload;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindObjectMetadataToHeaders;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindRemoveObjectMetadataToHeaders;
import org.jclouds.openstack.swift.v1.binders.SetPayload;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.functions.ETagHeader;
import org.jclouds.openstack.swift.v1.functions.ParseObjectFromResponse;
import org.jclouds.openstack.swift.v1.functions.ParseObjectListFromResponse;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.openstack.swift.v1.options.PutOptions;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import com.google.common.annotations.Beta;
/**
* Provides access to the OpenStack Object Storage (Swift) Object API features.
* <p/>
* This API is new to jclouds and hence is in Beta. That means we need people to use it and give us feedback. Based
* on that feedback, minor changes to the interfaces may happen. This code will replace
* org.jclouds.openstack.swift.SwiftClient in jclouds 2.0 and it is recommended you adopt it sooner than later.
*/
@Beta
@RequestFilters(AuthenticateRequest.class)
@Consumes(APPLICATION_JSON)
public interface ObjectApi {
/**
* Lists up to 10,000 objects.
*
* @return an {@link ObjectList} of {@link SwiftObject} ordered by name or {@code null}.
*/
@Named("object:list")
@GET
@ResponseParser(ParseObjectListFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
@Nullable
ObjectList list();
/**
* Lists up to 10,000 objects. To control a large list of containers beyond
* 10,000 objects, use the {@code marker} and {@code endMarker} parameters in the
* {@link ListContainerOptions} class.
*
* @param options
* the {@link ListContainerOptions} for controlling the returned list.
*
* @return an {@link ObjectList} of {@link SwiftObject} ordered by name or {@code null}.
*/
@Named("object:list")
@GET
@ResponseParser(ParseObjectListFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
@Nullable
ObjectList list(ListContainerOptions options);
/**
* Creates or updates a {@link SwiftObject}.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
* @param payload
* corresponds to {@link SwiftObject#getPayload()}.
*
* @return {@link SwiftObject#getETag()} of the object.
*/
@Named("object:put")
@PUT
@Path("/{objectName}")
@Headers(keys = EXPECT, values = "100-continue")
@ResponseParser(ETagHeader.class)
String put(@PathParam("objectName") String objectName, @BinderParam(SetPayload.class) Payload payload);
/**
* Creates or updates a {@link SwiftObject}.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
* @param payload
* corresponds to {@link SwiftObject#getPayload()}.
* @param options
* {@link PutOptions options} to control creating the {@link SwiftObject}.
*
* @return {@link SwiftObject#getETag()} of the object.
*/
@Named("object:put")
@PUT
@Path("/{objectName}")
@Headers(keys = EXPECT, values = "100-continue")
@ResponseParser(ETagHeader.class)
String put(@PathParam("objectName") String objectName, @BinderParam(SetPayload.class) Payload payload,
PutOptions options);
/**
* Gets the {@link SwiftObject} metadata without its {@link Payload#openStream() body}.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
*
* @return the {@link SwiftObject} or {@code null}, if not found.
*/
@Named("object:getWithoutBody")
@HEAD
@Path("/{objectName}")
@ResponseParser(ParseObjectFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
@Nullable
SwiftObject getWithoutBody(@PathParam("objectName") String objectName);
/**
* Gets the {@link SwiftObject} including its {@link Payload#openStream() body}.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
*
* @return the {@link SwiftObject} or {@code null}, if not found.
*/
@Named("object:get")
@GET
@Path("/{objectName}")
@ResponseParser(ParseObjectFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
@Nullable
SwiftObject get(@PathParam("objectName") String objectName);
/**
* Gets the {@link SwiftObject} including its {@link Payload#openStream() body}.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
* @param options
* options to control the download.
*
* @return the {@link SwiftObject} or {@code null}, if not found.
*/
@Named("object:get")
@GET
@Path("/{objectName}")
@ResponseParser(ParseObjectFromResponse.class)
@Fallback(NullOnNotFoundOr404.class)
@Nullable
SwiftObject get(@PathParam("objectName") String objectName, GetOptions options);
/**
* Creates or updates the metadata for a {@link SwiftObject}.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
* @param metadata
* the metadata to create or update.
*
* @return {@code true} if the metadata was successfully created or updated,
* {@code false} if not.
*/
@Named("object:updateMetadata")
@POST
@Path("/{objectName}")
@Produces("")
@Fallback(FalseOnNotFoundOr404.class)
boolean updateMetadata(@PathParam("objectName") String objectName,
@BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> metadata);
/**
* Deletes the metadata from a {@link SwiftObject}.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
* @param metadata
* corresponds to {@link SwiftObject#getMetadata()}.
*
* @return {@code true} if the metadata was successfully deleted,
* {@code false} if not.
*/
@Named("object:deleteMetadata")
@POST
@Path("/{objectName}")
@Fallback(FalseOnNotFoundOr404.class)
boolean deleteMetadata(@PathParam("objectName") String objectName,
@BinderParam(BindRemoveObjectMetadataToHeaders.class) Map<String, String> metadata);
/**
* Deletes an object, if present.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
*/
@Named("object:delete")
@DELETE
@Path("/{objectName}")
@Fallback(VoidOnNotFoundOr404.class)
void delete(@PathParam("objectName") String objectName);
/**
* Copies an object from one container to another.
*
* <h3>NOTE</h3>
* This is a server side copy.
*
* @param destinationObject
* the destination object name.
* @param sourceContainer
* the source container name.
* @param sourceObject
* the source object name.
*
* @return {@code true} if the object was successfully copied, {@code false} if not.
*
* @throws org.jclouds.openstack.swift.v1.CopyObjectException if the source or destination container do not exist.
*/
@Named("object:copy")
@PUT
@Path("/{destinationObject}")
@Headers(keys = OBJECT_COPY_FROM, values = "/{sourceContainer}/{sourceObject}")
@Fallback(FalseOnContainerNotFound.class)
boolean copy(@PathParam("destinationObject") String destinationObject,
@PathParam("sourceContainer") String sourceContainer,
@PathParam("sourceObject") String sourceObject);
}

View File

@ -0,0 +1,90 @@
/*
* 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.openstack.swift.v1.features;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import java.util.List;
import java.util.Map;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindObjectMetadataToHeaders;
import org.jclouds.openstack.swift.v1.domain.Segment;
import org.jclouds.openstack.swift.v1.functions.ETagHeader;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.annotations.Beta;
/**
* Provides access to the OpenStack Object Storage (Swift) Static Large Object API features.
* <p/>
* This API is new to jclouds and hence is in Beta. That means we need people to use it and give us feedback. Based
* on that feedback, minor changes to the interfaces may happen. This code will replace
* org.jclouds.openstack.swift.SwiftClient in jclouds 2.0 and it is recommended you adopt it sooner than later.
*/
@Beta
@RequestFilters(AuthenticateRequest.class)
@Consumes(APPLICATION_JSON)
@Path("/{objectName}")
public interface StaticLargeObjectApi {
/**
* Creates or updates a static large object's manifest.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
* @param segments
* ordered parts which will be concatenated upon download.
* @param metadata
* corresponds to {@link SwiftObject#getMetadata()}.
*
* @return {@link SwiftObject#getEtag()} of the object, which is the MD5
* checksum of the concatenated ETag values of the {@code segments}.
*/
@Named("staticLargeObject:replaceManifest")
@PUT
@ResponseParser(ETagHeader.class)
@QueryParams(keys = "multipart-manifest", values = "put")
String replaceManifest(@PathParam("objectName") String objectName,
@BinderParam(BindToJsonPayload.class) List<Segment> segments,
@BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> metadata);
/**
* Deletes a static large object, if present, including all of its segments.
*
* @param objectName
* corresponds to {@link SwiftObject#getName()}.
*/
@Named("staticLargeObject:delete")
@DELETE
@Fallback(VoidOnNotFoundOr404.class)
@QueryParams(keys = "multipart-manifest", values = "delete")
void delete(@PathParam("objectName") String objectName);
}

View File

@ -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.openstack.swift.v1.functions;
import static com.google.common.net.HttpHeaders.ETAG;
import org.jclouds.http.HttpResponse;
import com.google.common.base.Function;
public class ETagHeader implements Function<HttpResponse, String> {
@Override
public String apply(HttpResponse from) {
String etag = from.getFirstHeaderOrNull(ETAG);
return etag != null ? etag.replace("\"", "") : null;
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.openstack.swift.v1.functions;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
/**
* Extracts entries whose keys start with {@code .*-Meta-}.
*
* @param from
* a {@link Multimap} containing the prefixed headers.
*
* @return the extracted metadata without the prefixed keys.
*/
public enum EntriesWithoutMetaPrefix implements Function<Multimap<String, String>, Map<String, String>> {
INSTANCE;
@Override
public Map<String, String> apply(Multimap<String, String> arg0) {
ImmutableMap.Builder<String, String> metadata = ImmutableMap.builder();
for (Entry<String, String> header : arg0.entries()) {
int index = header.getKey().indexOf("-Meta-");
if (index != -1) {
metadata.put(header.getKey().substring(index + 6), header.getValue());
}
}
return metadata.build();
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.openstack.swift.v1.functions;
import org.jclouds.http.HttpResponse;
import com.google.common.base.Function;
/** Returns {@code false} on HTTP 202 {@code Accepted}. */
public class FalseOnAccepted implements Function<HttpResponse, Boolean> {
@Override
public Boolean apply(HttpResponse from) {
return from.getStatusCode() == 202 ? false : true;
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.openstack.swift.v1.functions;
import java.util.Map;
import org.jclouds.http.HttpResponse;
import com.google.common.base.Function;
/** Extracts metadata entries from http response headers. */
public class MetadataFromHeaders implements Function<HttpResponse, Map<String, String>> {
@Override
public Map<String, String> apply(HttpResponse from) {
return EntriesWithoutMetaPrefix.INSTANCE.apply(from.getHeaders());
}
}

View File

@ -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.openstack.swift.v1.functions;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_BYTES_USED;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_CONTAINER_COUNT;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_OBJECT_COUNT;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.swift.v1.domain.Account;
import com.google.common.base.Function;
public class ParseAccountFromHeaders implements Function<HttpResponse, Account> {
@Override
public Account apply(HttpResponse from) {
return Account.builder()
.bytesUsed(Long.parseLong(from.getFirstHeaderOrNull(ACCOUNT_BYTES_USED)))
.containerCount(Long.parseLong(from.getFirstHeaderOrNull(ACCOUNT_CONTAINER_COUNT)))
.objectCount(Long.parseLong(from.getFirstHeaderOrNull(ACCOUNT_OBJECT_COUNT)))
.metadata(EntriesWithoutMetaPrefix.INSTANCE.apply(from.getHeaders())).build();
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.openstack.swift.v1.functions;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_BYTES_USED;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_OBJECT_COUNT;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
public class ParseContainerFromHeaders implements Function<HttpResponse, Container>,
InvocationContext<ParseContainerFromHeaders> {
String name;
@Override
public Container apply(HttpResponse from) {
Container c =
Container.builder()
.name(name)
.bytesUsed(Long.parseLong(from.getFirstHeaderOrNull(CONTAINER_BYTES_USED)))
.objectCount(Long.parseLong(from.getFirstHeaderOrNull(CONTAINER_OBJECT_COUNT)))
.anybodyRead(CONTAINER_ACL_ANYBODY_READ.equals(from.getFirstHeaderOrNull(CONTAINER_READ)))
.metadata(EntriesWithoutMetaPrefix.INSTANCE.apply(from.getHeaders())).build();
return c;
}
@Override
public ParseContainerFromHeaders setContext(HttpRequest request) {
this.name = GeneratedHttpRequest.class.cast(request).getInvocation().getArgs().get(0).toString();
return this;
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.openstack.swift.v1.functions;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.net.HttpHeaders.ETAG;
import static com.google.common.net.HttpHeaders.LAST_MODIFIED;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_DELETE_AT;
import java.net.URI;
import java.util.Date;
import javax.inject.Inject;
import org.jclouds.date.DateService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
import com.google.common.hash.HashCode;
public class ParseObjectFromResponse implements Function<HttpResponse, SwiftObject>,
InvocationContext<ParseObjectFromResponse> {
private final DateService dates;
@Inject
ParseObjectFromResponse(DateService dates) {
this.dates = dates;
}
private String uri;
private String name;
@Override
public SwiftObject apply(HttpResponse from) {
Payload payload = from.getPayload();
MutableContentMetadata contentMeta = payload.getContentMetadata();
String deleteAt = from.getFirstHeaderOrNull(OBJECT_DELETE_AT);
if (deleteAt != null) {
long fromEpoch = Long.parseLong(from.getFirstHeaderOrNull(OBJECT_DELETE_AT)) * 1000;
contentMeta.setExpires(new Date(fromEpoch));
payload.setContentMetadata(contentMeta);
}
String etag = from.getFirstHeaderOrNull(ETAG);
if (etag != null) {
payload.getContentMetadata().setContentMD5(HashCode.fromBytes(base16().lowerCase().decode(etag)));
}
return SwiftObject.builder()
.uri(URI.create(uri))
.name(name)
.etag(etag)
.payload(payload)
.lastModified(dates.rfc822DateParse(from.getFirstHeaderOrNull(LAST_MODIFIED)))
.headers(from.getHeaders())
.metadata(EntriesWithoutMetaPrefix.INSTANCE.apply(from.getHeaders())).build();
}
@Override
public ParseObjectFromResponse setContext(HttpRequest request) {
this.uri = request.getEndpoint().toString();
this.name = GeneratedHttpRequest.class.cast(request).getInvocation().getArgs().get(0).toString();
return this;
}
}

View File

@ -0,0 +1,113 @@
/*
* 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.openstack.swift.v1.functions;
import static com.google.common.io.BaseEncoding.base16;
import static org.jclouds.http.Uris.uriBuilder;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.hash.HashCode;
import com.google.common.io.ByteSource;
public class ParseObjectListFromResponse implements Function<HttpResponse, ObjectList>,
InvocationContext<ParseObjectListFromResponse> {
private static final class InternalObject {
String name;
String hash;
long bytes;
String content_type;
Date last_modified;
Date expires;
}
private final ParseJson<List<InternalObject>> json;
private final ParseContainerFromHeaders parseContainer;
@Inject
ParseObjectListFromResponse(ParseJson<List<InternalObject>> json, ParseContainerFromHeaders parseContainer) {
this.json = json;
this.parseContainer = parseContainer;
}
private ToSwiftObject toSwiftObject;
@Override
public ObjectList apply(HttpResponse from) {
List<SwiftObject> objects = Lists.transform(json.apply(from), toSwiftObject);
Container container = parseContainer.apply(from);
return ObjectList.create(objects, container);
}
static class ToSwiftObject implements Function<InternalObject, SwiftObject> {
private final String containerUri;
ToSwiftObject(String containerUri) {
this.containerUri = containerUri;
}
@Override
public SwiftObject apply(InternalObject input) {
return SwiftObject.builder()
.uri(uriBuilder(containerUri).clearQuery().appendPath(input.name).build())
.name(input.name)
.etag(input.hash)
.payload(payload(input.bytes, input.hash, input.content_type, input.expires))
.lastModified(input.last_modified).build();
}
}
@Override
public ParseObjectListFromResponse setContext(HttpRequest request) {
parseContainer.name = GeneratedHttpRequest.class.cast(request).getCaller().get().getArgs().get(1).toString();
String containerUri = request.getEndpoint().toString();
int queryIndex = containerUri.indexOf('?');
if (queryIndex != -1) {
containerUri = containerUri.substring(0, queryIndex);
}
toSwiftObject = new ToSwiftObject(containerUri);
return this;
}
private static Payload payload(long bytes, String hash, String contentType, Date expires) {
Payload payload = Payloads.newByteSourcePayload(ByteSource.empty());
payload.getContentMetadata().setContentLength(bytes);
payload.getContentMetadata().setContentType(contentType);
payload.getContentMetadata().setExpires(expires);
if (hash != null) {
payload.getContentMetadata().setContentMD5(HashCode.fromBytes(base16().lowerCase().decode(hash)));
}
return payload;
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.openstack.swift.v1.handlers;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.openstack.swift.v1.CopyObjectException;
import org.jclouds.openstack.swift.v1.reference.SwiftHeaders;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.InsufficientResourcesException;
// TODO: is there error spec someplace? let's type errors, etc.
public class SwiftErrorHandler implements HttpErrorHandler {
public static final String PREFIX = "^/v[0-9][^/]*/[a-zA-Z]+_[^/]+/";
public static final Pattern CONTAINER_PATH = Pattern.compile(PREFIX + "([^/]+)$");
public static final Pattern CONTAINER_KEY_PATH = Pattern.compile(PREFIX + "([^/]+)/(.*)");
public void handleError(HttpCommand command, HttpResponse response) {
// it is important to always read fully and close streams
byte[] data = closeClientButKeepContentStream(response);
String message = data != null ? new String(data) : null;
Exception exception = message != null ? new HttpResponseException(command, response, message)
: new HttpResponseException(command, response);
message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
response.getStatusLine());
switch (response.getStatusCode()) {
case 401:
exception = new AuthorizationException(exception.getMessage(), exception);
break;
case 404:
Exception oldException = exception;
String sourcePath = command.getCurrentRequest().getFirstHeaderOrNull(SwiftHeaders.OBJECT_COPY_FROM);
if (sourcePath != null) {
// the path returned here is in the form "/v1/tenant-id/destContainer/destObject"
String path = command.getCurrentRequest().getEndpoint().getPath();
int startOfDestinationPath = path.lastIndexOf("/", path.lastIndexOf("/") - 1);
// get the "/destContainer/destObject" portion of the path
String destinationPath = path.substring(startOfDestinationPath);
exception = new CopyObjectException(sourcePath, destinationPath, message);
exception.initCause(oldException);
} else if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
String path = command.getCurrentRequest().getEndpoint().getPath();
Matcher matcher = CONTAINER_PATH.matcher(path);
if (matcher.find()) {
exception = new ContainerNotFoundException(matcher.group(1), message);
exception.initCause(oldException);
} else {
matcher = CONTAINER_KEY_PATH.matcher(path);
if (matcher.find()) {
exception = new KeyNotFoundException(matcher.group(1), matcher.group(2), message);
exception.initCause(oldException);
}
}
}
break;
case 409:
exception = new IllegalStateException(exception.getMessage(), exception);
break;
case 413:
exception = new InsufficientResourcesException(exception.getMessage(), exception);
break;
}
command.setException(exception);
}
}

View File

@ -0,0 +1,108 @@
/*
* 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.openstack.swift.v1.options;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
import java.util.Map;
import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
import com.google.common.collect.Multimap;
/**
* Options for creating a {@link Container}
*
* @see ContainerApi#create(String, CreateContainerOptions)
*/
public class CreateContainerOptions extends BaseHttpRequestOptions {
public static final CreateContainerOptions NONE = new CreateContainerOptions();
/**
* Sets the headers on a container at creation.
*/
public CreateContainerOptions headers(Multimap<String, String> headers) {
this.headers.putAll(headers);
return this;
}
/**
* Sets the metadata on a container at creation.
*/
public CreateContainerOptions metadata(Map<String, String> metadata) {
this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
return this;
}
/**
* Sets the public ACL on the container so that anybody can read it.
*/
public CreateContainerOptions anybodyRead() {
this.headers.put(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ);
return this;
}
/**
* Sets the container that will contain object versions.
*/
public CreateContainerOptions versionsLocation(String containerName) {
this.headers.put(VERSIONS_LOCATION, containerName);
return this;
}
public static class Builder {
/**
* @see CreateContainerOptions#anybodyRead
*/
public static CreateContainerOptions anybodyRead() {
CreateContainerOptions options = new CreateContainerOptions();
return options.anybodyRead();
}
/**
* @see CreateContainerOptions#headers
*/
public static CreateContainerOptions headers(Multimap<String, String> headers) {
CreateContainerOptions options = new CreateContainerOptions();
return options.headers(headers);
}
/**
* @see CreateContainerOptions#metadata
*/
public static CreateContainerOptions metadata(Map<String, String> metadata) {
CreateContainerOptions options = new CreateContainerOptions();
return options.metadata(metadata);
}
/**
* @see CreateContainerOptions#versionsLocation
*/
public static CreateContainerOptions versionsLocation(String containerName) {
CreateContainerOptions options = new CreateContainerOptions();
return options.versionsLocation(containerName);
}
}
private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(CONTAINER_METADATA_PREFIX);
}

View File

@ -0,0 +1,132 @@
/*
* 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.openstack.swift.v1.options;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import org.jclouds.http.options.BaseHttpRequestOptions;
/**
* Options for listing containers.
*
* @see ContainerApi#list(ListContainerOptions)
*/
public class ListContainerOptions extends BaseHttpRequestOptions {
public static final ListContainerOptions NONE = new ListContainerOptions();
/**
* list operation returns no more than this amount.
*/
public ListContainerOptions limit(int limit) {
checkState(limit >= 0, "limit must be >= 0");
checkState(limit <= 10000, "limit must be <= 10000");
queryParameters.put("limit", Integer.toString(limit));
return this;
}
/**
* object names greater in value than the specified marker are returned.
*/
public ListContainerOptions marker(String marker) {
queryParameters.put("marker", checkNotNull(marker, "marker"));
return this;
}
/**
* object names less in value than the specified marker are returned.
*/
public ListContainerOptions endMarker(String endMarker) {
queryParameters.put("end_marker", checkNotNull(endMarker, "endMarker"));
return this;
}
/**
* object names beginning with this substring are returned.
*/
public ListContainerOptions prefix(String prefix) {
queryParameters.put("prefix", checkNotNull(prefix, "prefix"));
return this;
}
/**
* object names nested in the container are returned.
*/
public ListContainerOptions delimiter(char delimiter) {
queryParameters.put("delimiter", Character.toString(delimiter));
return this;
}
/**
* object names nested in the pseudo path are returned.
*/
public ListContainerOptions path(String path) {
queryParameters.put("path", checkNotNull(path, "path"));
return this;
}
public static class Builder {
/**
* @see ListContainerOptions#limit
*/
public static ListContainerOptions limit(int limit) {
ListContainerOptions options = new ListContainerOptions();
return options.limit(limit);
}
/**
* @see ListContainerOptions#marker
*/
public static ListContainerOptions marker(String marker) {
ListContainerOptions options = new ListContainerOptions();
return options.marker(marker);
}
/**
* @see ListContainerOptions#endMarker
*/
public static ListContainerOptions endMarker(String endMarker) {
ListContainerOptions options = new ListContainerOptions();
return options.endMarker(endMarker);
}
/**
* @see ListContainerOptions#prefix
*/
public static ListContainerOptions prefix(String prefix) {
ListContainerOptions options = new ListContainerOptions();
return options.prefix(prefix);
}
/**
* @see ListContainerOptions#delimiter
*/
public static ListContainerOptions delimiter(char delimiter) {
ListContainerOptions options = new ListContainerOptions();
return options.delimiter(delimiter);
}
/**
* @see ListContainerOptions#path
*/
public static ListContainerOptions path(String path) {
ListContainerOptions options = new ListContainerOptions();
return options.path(path);
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.openstack.swift.v1.options;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_METADATA_PREFIX;
import java.util.Map;
import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
import com.google.common.collect.Multimap;
/**
* Options for creating an Object.
*/
public class PutOptions extends BaseHttpRequestOptions {
public static final PutOptions NONE = new PutOptions();
/**
* Sets the metadata on a container at creation.
*/
public PutOptions metadata(Map<String, String> metadata) {
this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
return this;
}
/**
* Sets the headers on a container at creation.
*/
public PutOptions headers(Multimap<String, String> headers) {
this.headers.putAll(headers);
return this;
}
public static class Builder {
/**
* @see PutOptions#headers
*/
public static PutOptions headers(Multimap<String, String> headers) {
PutOptions options = new PutOptions();
return options.headers(headers);
}
/**
* @see PutOptions#metadata
*/
public static PutOptions metadata(Map<String, String> metadata) {
PutOptions options = new PutOptions();
return options.metadata(metadata);
}
}
private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(OBJECT_METADATA_PREFIX);
}

View File

@ -0,0 +1,107 @@
/*
* 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.openstack.swift.v1.options;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
import java.util.Map;
import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders;
import com.google.common.collect.Multimap;
/**
* Options for updating a {@link Container}.
*
* @see org.jclouds.openstack.swift.v1.features.ContainerApi#update(String, UpdateContainerOptions)
*/
public class UpdateContainerOptions extends BaseHttpRequestOptions {
public static final UpdateContainerOptions NONE = new UpdateContainerOptions();
/**
* Sets the headers on a container at creation.
*/
public UpdateContainerOptions headers(Multimap<String, String> headers) {
this.headers.putAll(headers);
return this;
}
/**
* Sets the metadata on a container at creation.
*/
public UpdateContainerOptions metadata(Map<String, String> metadata) {
this.headers.putAll(bindMetadataToHeaders.toHeaders(metadata));
return this;
}
/**
* Sets the public ACL on the container so that anybody can read it.
*/
public UpdateContainerOptions anybodyRead() {
this.headers.put(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ);
return this;
}
/**
* Sets the container that will contain object versions.
*/
public UpdateContainerOptions versionsLocation(String containerName) {
this.headers.put(VERSIONS_LOCATION, containerName);
return this;
}
public static class Builder {
/**
* @see UpdateContainerOptions#anybodyRead
*/
public static UpdateContainerOptions anybodyRead() {
UpdateContainerOptions options = new UpdateContainerOptions();
return options.anybodyRead();
}
/**
* @see UpdateContainerOptions#headers
*/
public static UpdateContainerOptions headers(Multimap<String, String> headers) {
UpdateContainerOptions options = new UpdateContainerOptions();
return options.headers(headers);
}
/**
* @see UpdateContainerOptions#metadata
*/
public static UpdateContainerOptions metadata(Map<String, String> metadata) {
UpdateContainerOptions options = new UpdateContainerOptions();
return options.metadata(metadata);
}
/**
* @see UpdateContainerOptions#versionsLocation
*/
public static UpdateContainerOptions versionsLocation(String containerName) {
UpdateContainerOptions options = new UpdateContainerOptions();
return options.versionsLocation(containerName);
}
}
private static final BindMetadataToHeaders bindMetadataToHeaders = new BindMetadataToHeaders(CONTAINER_METADATA_PREFIX);
}

View File

@ -0,0 +1,92 @@
/*
* 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.openstack.swift.v1.reference;
/**
* Common headers in Swift.
*/
public final class SwiftHeaders {
// Common Metadata Prefixes
public static final String ACCOUNT_METADATA_PREFIX = "X-Account-Meta-";
public static final String CONTAINER_METADATA_PREFIX = "X-Container-Meta-";
public static final String OBJECT_METADATA_PREFIX = "X-Object-Meta-";
public static final String USER_METADATA_PREFIX = OBJECT_METADATA_PREFIX;
// Metadata Removal Prefixes
public static final String ACCOUNT_REMOVE_METADATA_PREFIX = "X-Remove-Account-Meta-";
public static final String CONTAINER_REMOVE_METADATA_PREFIX = "X-Remove-Container-Meta-";
public static final String OBJECT_REMOVE_METADATA_PREFIX = "X-Remove-Object-Meta-";
// TempURL
public static final String ACCOUNT_TEMPORARY_URL_KEY = ACCOUNT_METADATA_PREFIX + "Temp-Url-Key";
public static final String ACCOUNT_TEMPORARY_URL_KEY_2 = ACCOUNT_TEMPORARY_URL_KEY + "-2";
// Account Headers
public static final String ACCOUNT_BYTES_USED = "X-Account-Bytes-Used";
public static final String ACCOUNT_CONTAINER_COUNT = "X-Account-Container-Count";
public static final String ACCOUNT_OBJECT_COUNT = "X-Account-Object-Count";
// Container Headers
public static final String CONTAINER_BYTES_USED = "X-Container-Bytes-Used";
public static final String CONTAINER_OBJECT_COUNT = "X-Container-Object-Count";
// Public access - not supported in all Swift Impls
public static final String CONTAINER_READ = "X-Container-Read";
public static final String CONTAINER_WRITE = "X-Container-Write";
public static final String CONTAINER_ACL_ANYBODY_READ = ".r:*,.rlistings";
// CORS
public static final String CONTAINER_ACCESS_CONTROL_ALLOW_ORIGIN = CONTAINER_METADATA_PREFIX + "Access-Control-Allow-Origin";
public static final String CONTAINER_ACCESS_CONTROL_MAX_AGE = CONTAINER_METADATA_PREFIX + "Access-Control-Max-Age";
public static final String CONTAINER_ACCESS_CONTROL_EXPOSE_HEADERS = CONTAINER_METADATA_PREFIX + "Access-Control-Expose-Headers";
// Container Quota
public static final String CONTAINER_QUOTA_BYTES = CONTAINER_METADATA_PREFIX + "Quota-Bytes";
public static final String CONTAINER_QUOTA_COUNT = CONTAINER_METADATA_PREFIX + "Quota-Count";
// Container Sync
public static final String CONTAINER_SYNC_KEY = "X-Container-Sync-Key";
public static final String CONTAINER_SYNC_TO = "X-Container-Sync-To";
// Versioning
public static final String VERSIONS_LOCATION = "X-Versions-Location";
// Misc functionality
public static final String CONTAINER_WEB_MODE = "X-Web-Mode";
public static final String OBJECT_COPY_FROM = "X-Copy-From";
public static final String OBJECT_DELETE_AFTER = "X-Delete-After";
public static final String OBJECT_DELETE_AT = "X-Delete-At";
public static final String OBJECT_MANIFEST = "X-Object-Manifest";
/** Get the newest version of the object for GET and HEAD requests */
public static final String OBJECT_NEWEST = "X-Newest";
// Static Large Object
public static final String STATIC_LARGE_OBJECT = "X-Static-Large-Object";
// Static Web
public static final String STATIC_WEB_INDEX = CONTAINER_METADATA_PREFIX + "Web-Index";
public static final String STATIC_WEB_DIRECTORY_TYPE = CONTAINER_METADATA_PREFIX + "Web-Directory-Type";
public static final String STATIC_WEB_ERROR = CONTAINER_METADATA_PREFIX + "Web-Error";
public static final String STATIC_WEB_LISTINGS = CONTAINER_METADATA_PREFIX + "Web-Listings";
public static final String STATIC_WEB_LISTINGS_CSS = CONTAINER_METADATA_PREFIX + "Web-Listings-CSS";
private SwiftHeaders() {
throw new AssertionError("intentionally unimplemented");
}
}

View File

@ -0,0 +1,18 @@
#
# 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.
#
org.jclouds.openstack.swift.v1.SwiftApiMetadata

View File

@ -0,0 +1,71 @@
/*
* 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.openstack.swift.v1;
import static com.google.common.base.Charsets.UTF_8;
import static org.jclouds.openstack.swift.v1.features.AccountApiMockTest.accountResponse;
import static org.testng.Assert.assertEquals;
import java.util.Properties;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
/**
* @see KeystoneProperties#CREDENTIAL_TYPE
*/
@Test
public class AuthenticationMockTest extends BaseOpenStackMockTest<SwiftApi> {
@DataProvider(name = "jclouds.keystone.credential-type")
Object[][] credentialTypeToPostBody() {
Object[][] credentialTypeToPostBody = new Object[2][2];
credentialTypeToPostBody[0][0] = "apiAccessKeyCredentials";
credentialTypeToPostBody[0][1] = "{\"auth\":{\"apiAccessKeyCredentials\":{\"accessKey\":\"joe\",\"secretKey\":\"letmein\"},\"tenantName\":\"jclouds\"}}";
credentialTypeToPostBody[1][0] = "passwordCredentials";
credentialTypeToPostBody[1][1] = "{\"auth\":{\"passwordCredentials\":{\"username\":\"joe\",\"password\":\"letmein\"},\"tenantName\":\"jclouds\"}}";
return credentialTypeToPostBody;
}
@Test(dataProvider = "jclouds.keystone.credential-type")
public void authenticateCredentialType(String credentialType, String expectedPost) throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(accountResponse()));
try {
Properties overrides = new Properties();
overrides.setProperty("jclouds.keystone.credential-type", credentialType);
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift", overrides);
api.getAccountApi("DFW").get();
assertEquals(server.getRequestCount(), 2);
RecordedRequest authRequest = server.takeRequest();
assertEquals(authRequest.getRequestLine(), "POST /tokens HTTP/1.1");
assertEquals(new String(authRequest.getBody(), UTF_8), expectedPost);
} finally {
server.shutdown();
}
}
}

View File

@ -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.openstack.swift.v1;
import org.jclouds.View;
import org.jclouds.apis.internal.BaseApiMetadataTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.TypeToken;
@Test(groups = "unit", testName = "SwiftApiMetadataTest")
// public class SwiftApiMetadataTest extends BaseBlobStoreApiMetadataTest {
public class SwiftApiMetadataTest extends BaseApiMetadataTest {
public SwiftApiMetadataTest() {
super(new SwiftApiMetadata(), ImmutableSet.<TypeToken<? extends View>> of());
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.openstack.swift.v1;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reportMatcher;
import static org.easymock.EasyMock.verify;
import static org.jclouds.io.Payloads.newByteSourcePayload;
import java.net.URI;
import org.easymock.IArgumentMatcher;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.swift.v1.handlers.SwiftErrorHandler;
import org.testng.annotations.Test;
import com.google.common.io.ByteSource;
/**
* Tests the {@link SwiftErrorHandler}
*/
@Test(groups = "unit", testName = "SwiftErrorHandlerTest")
public class SwiftErrorHandlerTest {
@Test
public void test404SetsKeyNotFoundExceptionMosso() {
assertCodeMakes("HEAD", URI
.create("http://host/v1/MossoCloudFS_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1/key"), 404,
"Not Found", "", KeyNotFoundException.class);
}
@Test
public void test404SetsKeyNotFoundExceptionSwift() {
assertCodeMakes("HEAD", URI
.create("http://67.202.39.175:8080/v1/AUTH_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1/key"),
404, "Not Found", "", KeyNotFoundException.class);
}
@Test
public void test404SetsContainerNotFoundExceptionMosso() {
assertCodeMakes("HEAD", URI
.create("http://host/v1/MossoCloudFS_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1"), 404,
"Not Found", "", ContainerNotFoundException.class);
}
@Test
public void test404SetsContainerNotFoundExceptionSwift() {
assertCodeMakes("HEAD", URI
.create("http://67.202.39.175:8080/v1/AUTH_7064cdb1d49d4dcba3c899ac33e8409d/adriancole-blobstore1"),
404, "Not Found", "", ContainerNotFoundException.class);
}
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String content,
Class<? extends Exception> expected) {
assertCodeMakes(method, uri, statusCode, message, "text/plain", content, expected);
}
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
String content, Class<? extends Exception> expected) {
SwiftErrorHandler function = new SwiftErrorHandler();
HttpCommand command = createMock(HttpCommand.class);
HttpRequest request = HttpRequest.builder().method(method).endpoint(uri).build();
HttpResponse response = HttpResponse.builder().statusCode(statusCode).message(message)
.payload(newByteSourcePayload(ByteSource.wrap(content.getBytes()))).build();
response.getPayload().getContentMetadata().setContentType(contentType);
expect(command.getCurrentRequest()).andReturn(request).atLeastOnce();
command.setException(classEq(expected));
replay(command);
function.handleError(command, response);
verify(command);
}
public static Exception classEq(final Class<? extends Exception> in) {
reportMatcher(new IArgumentMatcher() {
@Override
public void appendTo(StringBuffer buffer) {
buffer.append("classEq(");
buffer.append(in);
buffer.append(")");
}
@Override
public boolean matches(Object arg) {
return arg.getClass() == in;
}
});
return null;
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.openstack.swift.v1;
import static java.lang.String.format;
import static org.jclouds.io.Payloads.newByteSourcePayload;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.UUID;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.jclouds.util.Strings2;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.io.ByteSource;
@Test(groups = "live", testName = "TemporaryUrlSignerLiveTest")
public class TemporaryUrlSignerLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
private String name = getClass().getSimpleName();
private String containerName = getClass().getSimpleName() + "Container";
public void signForPublicAccess() throws Exception {
for (String regionId : api.getConfiguredRegions()) {
SwiftObject object = api.getObjectApi(regionId, containerName).get(name);
long expires = System.currentTimeMillis() / 1000 + 5;
String signature = TemporaryUrlSigner.checkApiEvery(api.getAccountApi(regionId), 5)
.sign("GET", object.getUri().getPath(), expires);
URI signed = URI.create(format("%s?temp_url_sig=%s&temp_url_expires=%s", object.getUri(), signature, expires));
InputStream publicStream = signed.toURL().openStream();
assertEquals(Strings2.toStringAndClose(publicStream), "swifty");
// let it expire
Thread.sleep(5000);
try {
signed.toURL().openStream();
fail("should have expired!");
} catch (IOException e) {
}
}
}
@Override
@BeforeClass(groups = "live")
public void setup() {
super.setup();
String key = UUID.randomUUID().toString();
for (String regionId : api.getConfiguredRegions()) {
api.getAccountApi(regionId).updateTemporaryUrlKey(key);
api.getContainerApi(regionId).create(containerName);
api.getObjectApi(regionId, containerName)
.put(name, newByteSourcePayload(ByteSource.wrap("swifty".getBytes())));
}
}
@AfterMethod
@AfterClass(groups = "live")
public void tearDown() {
for (String regionId : api.getConfiguredRegions()) {
api.getObjectApi(regionId, containerName).delete(name);
api.getContainerApi(regionId).deleteIfEmpty(containerName);
}
super.tearDown();
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.openstack.swift.v1;
import static org.jclouds.openstack.swift.v1.features.AccountApiMockTest.accountResponse;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_TEMPORARY_URL_KEY;
import static org.testng.Assert.assertEquals;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
import org.testng.annotations.Test;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
@Test(groups = "unit", testName = "TemporaryUrlSignerMockTest")
public class TemporaryUrlSignerMockTest extends BaseOpenStackMockTest<SwiftApi> {
@Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "accountApi")
public void whenAccountApiIsNull() {
TemporaryUrlSigner.checkApiEvery(null, 10000);
}
public void whenAccountApiHasKey() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(accountResponse().addHeader(ACCOUNT_TEMPORARY_URL_KEY, "mykey")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
String signature = TemporaryUrlSigner.checkApiEvery(api.getAccountApi("DFW"), 10000)
.sign("GET", "/v1/AUTH_account/container/object", 1323479485l);
assertEquals(signature, "d9fc2067e52b06598421664cf6610bfc8fc431f6");
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
assertEquals(server.takeRequest().getRequestLine(),
"HEAD /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9 HTTP/1.1");
} finally {
server.shutdown();
}
}
@Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = ".*returned a null temporaryUrlKey!")
public void whenAccountApiDoesntHaveKey() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(accountResponse()));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
TemporaryUrlSigner.checkApiEvery(api.getAccountApi("DFW"), 10000)
.sign("GET", "/v1/AUTH_account/container/object", 1323479485l);
} finally {
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
assertEquals(server.takeRequest().getRequestLine(),
"HEAD /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9 HTTP/1.1");
server.shutdown();
}
}
}

View File

@ -0,0 +1,159 @@
/*
* 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.openstack.swift.v1.blobstore;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import org.jclouds.blobstore.BlobRequestSigner;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
import org.jclouds.domain.Location;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.io.ByteStreams2;
import org.jclouds.io.MutableContentMetadata;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.rest.HttpClient;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteSource;
import com.google.common.hash.Hashing;
import com.google.common.net.MediaType;
import com.google.common.net.HttpHeaders;
@Test(groups = "live")
public class RegionScopedBlobStoreContextLiveTest extends BaseBlobStoreIntegrationTest {
public RegionScopedBlobStoreContextLiveTest() {
provider = "openstack-swift";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
@Test
public void testRegionsAreNotEmpty() {
assertFalse(RegionScopedBlobStoreContext.class.cast(view).getConfiguredRegions().isEmpty());
}
@Test
public void testLocationsMatch() {
RegionScopedBlobStoreContext ctx = RegionScopedBlobStoreContext.class.cast(view);
for (String regionId : ctx.getConfiguredRegions()) {
Set<? extends Location> locations = ctx.getBlobStore(regionId).listAssignableLocations();
assertEquals(locations.size(), 1, "expected one region " + regionId + " " + locations);
Location location = locations.iterator().next();
assertEquals(location.getId(), regionId, "region id " + regionId + " didn't match getId(): " + location);
}
}
@Test
public void testListBlobs() throws InterruptedException, ExecutionException {
RegionScopedBlobStoreContext ctx = RegionScopedBlobStoreContext.class.cast(view);
for (String regionId : ctx.getConfiguredRegions()) {
ctx.getBlobStore(regionId).list();
}
}
@Test
public void testSign() throws InterruptedException, ExecutionException,
IOException {
RegionScopedBlobStoreContext ctx = RegionScopedBlobStoreContext.class.cast(view);
for (String regionId : ctx.getConfiguredRegions()) {
BlobStore region = ctx.getBlobStore(regionId);
PageSet<? extends StorageMetadata> containers = region.list();
if (containers.isEmpty()) {
continue;
}
String containerName = Iterables.getLast(containers).getName();
final ByteSource input = ByteSource.wrap("str".getBytes());
final HttpClient client = ctx.utils().http();
// test signed put
String blobName = "test-" + UUID.randomUUID();
Blob blob2 = region.blobBuilder(blobName).forSigning()
.contentLength(input.size())
.contentMD5(input.hash(Hashing.md5()).asBytes())
.contentType(MediaType.OCTET_STREAM.toString()).build();
BlobRequestSigner signer = ctx.getSigner(regionId);
HttpResponse response;
try {
HttpRequest putRequest;
putRequest = signer.signPutBlob(containerName, blob2, 600);
MutableContentMetadata metadata = blob2.getMetadata()
.getContentMetadata();
HttpRequest.Builder<?> putRequestBuilder = putRequest.toBuilder()
.addHeader(HttpHeaders.CONTENT_TYPE,
metadata.getContentType());
putRequestBuilder.addHeader(HttpHeaders.CONTENT_LENGTH,
String.valueOf(input.size()));
putRequestBuilder.payload(input);
putRequest = putRequestBuilder.build();
Payload payload = Payloads.newPayload(input.read());
putRequest.setPayload(payload);
assertNotNull(putRequest, "regionId=" + regionId + ", container="
+ containerName + ", blob=" + blobName);
response = client.invoke(putRequest);
if (response.getStatusCode() != 200
&& response.getStatusCode() != 201) {
fail("Signed PUT expected to return 200 or 201 but returned "
+ response.getStatusCode());
}
} catch (Exception e) {
fail("Failed signed put test: " + e);
}
// test signed get
try {
HttpRequest getRequest = signer.signGetBlob(containerName,
blobName);
assertNotNull(getRequest, "regionId=" + regionId + ", container="
+ containerName + ", blob=" + blobName);
response = client.invoke(getRequest);
if (response.getStatusCode() != 200) {
fail("Signed GET expected to return 200 but returned "
+ response.getStatusCode());
}
Payload payload = response.getPayload();
assertEquals(ByteStreams2.toByteArrayAndClose(payload.openStream()), input.read(),
"Data with signed GET not identical to what was put");
} catch (Exception e) {
fail("Failed signed GET test: " + e);
}
}
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.openstack.swift.v1.blobstore.integration;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import java.util.Properties;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
import org.testng.SkipException;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "SwiftBlobIntegrationLiveTest")
public class SwiftBlobIntegrationLiveTest extends BaseBlobIntegrationTest {
public SwiftBlobIntegrationLiveTest() {
provider = "openstack-swift";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
// Object/Container name contains forbidden chars from "<>
@Override
@DataProvider(name = "delete")
public Object[][] createData() {
return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" }, { "path/foo" }, { "colon:" },
{ "asteri*k" }, { "p|pe" } };
}
@Override
public void testGetTwoRanges() {
throw new SkipException("unsupported in swift");
}
@Override
public void testCreateBlobWithExpiry() throws InterruptedException {
throw new SkipException("unsupported in swift");
}
@Test(groups = { "integration", "live" })
public void testGetIfUnmodifiedSince() throws InterruptedException {
throw new SkipException("unsupported in swift");
}
@Override
protected int getIncorrectContentMD5StatusCode() {
return 422;
}
@Override
protected void checkContentLanguage(Blob blob, String contentLanguage) {
assert blob.getPayload().getContentMetadata().getContentLanguage() == null;
assert blob.getMetadata().getContentMetadata().getContentLanguage() == null;
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.openstack.swift.v1.blobstore.integration;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import java.util.Properties;
import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "SwiftBlobLiveTest")
public class SwiftBlobLiveTest extends BaseBlobLiveTest {
public SwiftBlobLiveTest() {
provider = "openstack-swift";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.openstack.swift.v1.blobstore.integration;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import java.util.Properties;
import org.jclouds.blobstore.integration.internal.BaseBlobSignerLiveTest;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "SwiftBlobSignerLiveTest")
public class SwiftBlobSignerLiveTest extends BaseBlobSignerLiveTest {
public SwiftBlobSignerLiveTest() {
provider = "openstack-swift";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.openstack.swift.v1.blobstore.integration;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import static org.testng.Assert.assertTrue;
import java.util.Properties;
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "SwiftContainerIntegrationLiveTest")
public class SwiftContainerIntegrationLiveTest extends BaseContainerIntegrationTest {
public SwiftContainerIntegrationLiveTest() {
provider = "openstack-swift";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
@Override
public void testListRootUsesDelimiter() throws InterruptedException {
try {
super.testListRootUsesDelimiter();
} catch (AssertionError e) {
// swift doesn't have the "common prefixes" in the response that s3
// does. If we wanted this to pass, we'd need to create
// pseudo-directories implicitly, which is costly and troublesome. It
// is better to fail this assertion.
assertTrue(e.getMessage().matches(".*16.* but .*15.*"), e.getMessage());
// ^^ squishy regex to deal with various formats of testng messages.
}
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.openstack.swift.v1.blobstore.integration;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import java.util.Properties;
import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "SwiftContainerLiveTest")
public class SwiftContainerLiveTest extends BaseContainerLiveTest {
public SwiftContainerLiveTest() {
provider = "openstack-swift";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.openstack.swift.v1.blobstore.integration;
import static org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties.CREDENTIAL_TYPE;
import java.util.Properties;
import org.jclouds.blobstore.integration.internal.BaseServiceIntegrationTest;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "SwiftServiceIntegrationLiveTest")
public class SwiftServiceIntegrationLiveTest extends BaseServiceIntegrationTest {
public SwiftServiceIntegrationLiveTest() {
provider = "openstack-swift";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, CREDENTIAL_TYPE);
return props;
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.openstack.swift.v1.config;
import static org.testng.Assert.assertEquals;
import org.jclouds.openstack.swift.v1.config.SwiftTypeAdapters.BulkDeleteResponseAdapter;
import org.jclouds.openstack.swift.v1.config.SwiftTypeAdapters.ExtractArchiveResponseAdapter;
import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@Test
public class SwiftTypeAdaptersTest {
Gson gson = new GsonBuilder()
.registerTypeAdapter(ExtractArchiveResponse.class, new ExtractArchiveResponseAdapter())
.registerTypeAdapter(BulkDeleteResponse.class, new BulkDeleteResponseAdapter())
.create();
public void extractArchiveWithoutErrors() {
assertEquals(gson.fromJson(""
+ "{\n"
+ " \"Response Status\": \"201 Created\",\n"
+ " \"Response Body\": \"\",\n"
+ " \"Errors\": [],\n"
+ " \"Number Files Created\": 10\n"
+ "}", ExtractArchiveResponse.class), ExtractArchiveResponse.create(10, ImmutableMap.<String, String> of()));
}
public void extractArchiveWithErrorsAndDecodesPaths() {
assertEquals(
gson.fromJson(""
+ "{\n"
+ " \"Response Status\": \"201 Created\",\n"
+ " \"Response Body\": \"\",\n"
+ " \"Errors\": [\n"
+ " [\"/v1/12345678912345/mycontainer/home/xx%3Cyy\", \"400 Bad Request\"],\n"
+ " [\"/v1/12345678912345/mycontainer/../image.gif\", \"400 Bad Request\"]\n"
+ " ],\n"
+ " \"Number Files Created\": 8\n"
+ "}", ExtractArchiveResponse.class),
ExtractArchiveResponse.create(
8,
ImmutableMap.<String, String> builder()
.put("/v1/12345678912345/mycontainer/home/xx<yy", "400 Bad Request")
.put("/v1/12345678912345/mycontainer/../image.gif", "400 Bad Request").build()));
}
public void bulkDeleteWithoutErrors() {
assertEquals(gson.fromJson(""
+ "{\n"
+ " \"Response Status\": \"200 OK\",\n"
+ " \"Response Body\": \"\",\n"
+ " \"Errors\": [],\n"
+ " \"Number Not Found\": 1,\n"
+ " \"Number Deleted\": 9\n"
+ "}", BulkDeleteResponse.class), BulkDeleteResponse.create(9, 1, ImmutableMap.<String, String> of()));
}
public void bulkDeleteWithErrorsAndDecodesPaths() {
assertEquals(gson.fromJson(""
+ "{\n"
+ " \"Response Status\": \"400 Bad Request\",\n"
+ " \"Response Body\": \"\",\n"
+ " \"Errors\": [\n"
+ " [\"/v1/12345678912345/Not%20Empty\", \"409 Conflict\"]"
+ " ],\n"
+ " \"Number Deleted\": 0\n"
+ "}", BulkDeleteResponse.class),
BulkDeleteResponse.create(0, 0, ImmutableMap.of("/v1/12345678912345/Not Empty", "409 Conflict")));
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.openstack.swift.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.Account;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
@Test(groups = "live", testName = "AccountApiLiveTest")
public class AccountApiLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
public void testGet() throws Exception {
for (String regionId : regions) {
AccountApi accountApi = api.getAccountApi(regionId);
Account account = accountApi.get();
assertNotNull(account);
assertTrue(account.getContainerCount() >= 0);
assertTrue(account.getObjectCount() >= 0);
assertTrue(account.getBytesUsed() >= 0);
}
}
public void testUpdateMetadata() throws Exception {
for (String regionId : regions) {
AccountApi accountApi = api.getAccountApi(regionId);
Map<String, String> meta = ImmutableMap.of("MyAdd1", "foo", "MyAdd2", "bar");
assertTrue(accountApi.updateMetadata(meta));
accountHasMetadata(accountApi, meta);
}
}
public void testDeleteMetadata() throws Exception {
for (String regionId : regions) {
AccountApi accountApi = api.getAccountApi(regionId);
Map<String, String> meta = ImmutableMap.of("MyDelete1", "foo", "MyDelete2", "bar");
assertTrue(accountApi.updateMetadata(meta));
accountHasMetadata(accountApi, meta);
assertTrue(accountApi.deleteMetadata(meta));
Account account = accountApi.get();
for (Entry<String, String> entry : meta.entrySet()) {
// note keys are returned in lower-case!
assertFalse(account.getMetadata().containsKey(entry.getKey().toLowerCase()));
}
}
}
static void accountHasMetadata(AccountApi accountApi, Map<String, String> meta) {
Account account = accountApi.get();
for (Entry<String, String> entry : meta.entrySet()) {
// note keys are returned in lower-case!
assertEquals(account.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue(),
account + " didn't have metadata: " + entry);
}
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.openstack.swift.v1.features;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_BYTES_USED;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_CONTAINER_COUNT;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_OBJECT_COUNT;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_REMOVE_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.ACCOUNT_TEMPORARY_URL_KEY;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.Account;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
@Test(groups = "unit", testName = "AccountApiMockTest")
public class AccountApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
/** upper-cases first char, and lower-cases rest!! **/
public void getKnowingServerMessesWithMetadataKeyCaseFormat() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(accountResponse()
// note silly casing
.addHeader(ACCOUNT_METADATA_PREFIX + "Apiname", "swift")
.addHeader(ACCOUNT_METADATA_PREFIX + "Apiversion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
Account account = api.getAccountApi("DFW").get();
assertEquals(account.getContainerCount(), 3l);
assertEquals(account.getObjectCount(), 42l);
assertEquals(account.getBytesUsed(), 323479l);
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(account.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue());
}
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "HEAD", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9");
} finally {
server.shutdown();
}
}
public void updateMetadata() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(accountResponse()
.addHeader(ACCOUNT_METADATA_PREFIX + "ApiName", "swift")
.addHeader(ACCOUNT_METADATA_PREFIX + "ApiVersion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getAccountApi("DFW").updateMetadata(metadata));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
RecordedRequest replaceRequest = server.takeRequest();
assertRequest(replaceRequest, "POST", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9");
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(replaceRequest.getHeader(ACCOUNT_METADATA_PREFIX + entry.getKey().toLowerCase()), entry.getValue());
}
} finally {
server.shutdown();
}
}
public void updateTemporaryUrlKey() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(accountResponse()));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getAccountApi("DFW").updateTemporaryUrlKey("foobar"));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
RecordedRequest replaceRequest = server.takeRequest();
assertRequest(replaceRequest, "POST", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9");
assertEquals(replaceRequest.getHeader(ACCOUNT_TEMPORARY_URL_KEY), "foobar");
} finally {
server.shutdown();
}
}
public void deleteMetadata() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(accountResponse()));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getAccountApi("DFW").deleteMetadata(metadata));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"POST /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9 HTTP/1.1");
for (String key : metadata.keySet()) {
assertEquals(deleteRequest.getHeader(ACCOUNT_REMOVE_METADATA_PREFIX + key.toLowerCase()), "ignored");
}
} finally {
server.shutdown();
}
}
private static final Map<String, String> metadata = ImmutableMap.of("ApiName", "swift", "ApiVersion", "v1.1");
public static MockResponse accountResponse() {
return new MockResponse()
.addHeader(ACCOUNT_CONTAINER_COUNT, "3")
.addHeader(ACCOUNT_OBJECT_COUNT, "42")
.addHeader(ACCOUNT_BYTES_USED, "323479");
}
}

View File

@ -0,0 +1,125 @@
/*
* 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.openstack.swift.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import org.jboss.shrinkwrap.api.GenericArchive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.TarGzExporter;
import org.jclouds.io.ByteStreams2;
import org.jclouds.io.Payload;
import org.jclouds.io.payloads.ByteSourcePayload;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.ByteSource;
@Test(groups = "live", testName = "BulkApiLiveTest")
public class BulkApiLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
private static final int OBJECT_COUNT = 10;
private String containerName = getClass().getSimpleName();
List<String> paths = Lists.newArrayList();
byte[] tarGz;
public void testNotPresentWhenDeleting() throws Exception {
for (String regionId : regions) {
BulkDeleteResponse deleteResponse = api.getBulkApi(regionId).bulkDelete(
ImmutableList.of(UUID.randomUUID().toString()));
assertEquals(deleteResponse.getDeleted(), 0);
assertEquals(deleteResponse.getNotFound(), 1);
assertTrue(deleteResponse.getErrors().isEmpty());
}
}
public void testExtractArchive() throws Exception {
for (String regionId : regions) {
Payload payload = new ByteSourcePayload(ByteSource.wrap(tarGz));
ExtractArchiveResponse extractResponse = api.getBulkApi(regionId)
.extractArchive(containerName, payload, "tar.gz");
assertEquals(extractResponse.getCreated(), OBJECT_COUNT);
assertTrue(extractResponse.getErrors().isEmpty());
assertEquals(api.getContainerApi(regionId).get(containerName).getObjectCount(), OBJECT_COUNT);
// repeat the command
extractResponse = api.getBulkApi(regionId).extractArchive(containerName, payload, "tar.gz");
assertEquals(extractResponse.getCreated(), OBJECT_COUNT);
assertTrue(extractResponse.getErrors().isEmpty());
}
}
@Test(dependsOnMethods = "testExtractArchive")
public void testBulkDelete() throws Exception {
for (String regionId : regions) {
BulkDeleteResponse deleteResponse = api.getBulkApi(regionId).bulkDelete(paths);
assertEquals(deleteResponse.getDeleted(), OBJECT_COUNT);
assertEquals(deleteResponse.getNotFound(), 0);
assertTrue(deleteResponse.getErrors().isEmpty());
assertEquals(api.getContainerApi(regionId).get(containerName).getObjectCount(), 0);
}
}
@Override
@BeforeClass(groups = "live")
public void setup() {
super.setup();
for (String regionId : regions) {
boolean created = api.getContainerApi(regionId).create(containerName);
if (!created) {
deleteAllObjectsInContainer(regionId, containerName);
}
}
GenericArchive files = ShrinkWrap.create(GenericArchive.class, "files.tar.gz");
StringAsset content = new StringAsset("foo");
for (int i = 0; i < OBJECT_COUNT; i++) {
paths.add(containerName + "/file" + i);
files.add(content, "/file" + i);
}
try {
tarGz = ByteStreams2.toByteArrayAndClose(files.as(TarGzExporter.class).exportAsInputStream());
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
@Override
@AfterClass(groups = "live")
public void tearDown() {
for (String regionId : regions) {
deleteAllObjectsInContainer(regionId, containerName);
api.getContainerApi(regionId).deleteIfEmpty(containerName);
}
super.tearDown();
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.openstack.swift.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.jboss.shrinkwrap.api.GenericArchive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.TarGzExporter;
import org.jclouds.io.ByteStreams2;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
import org.testng.annotations.Test;
import com.google.common.io.ByteSource;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
@Test(groups = "unit", testName = "BulkApiMockTest")
public class BulkApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
public void testExtractArchive() throws Exception {
GenericArchive files = ShrinkWrap.create(GenericArchive.class, "files.tar.gz");
StringAsset content = new StringAsset("foo");
for (int i = 0; i < 10; i++) {
files.add(content, "/file" + i);
}
byte[] tarGz = ByteStreams2.toByteArrayAndClose(files.as(TarGzExporter.class).exportAsInputStream());
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201).setBody("{\"Number Files Created\": 10, \"Errors\": []}")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
Payload payload = Payloads.newByteSourcePayload(ByteSource.wrap(tarGz));
ExtractArchiveResponse response = api.getBulkApi("DFW").extractArchive("myContainer", payload, "tar.gz");
assertEquals(response.getCreated(), 10);
assertTrue(response.getErrors().isEmpty());
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
RecordedRequest extractRequest = server.takeRequest();
assertRequest(extractRequest, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer?extract-archive=tar.gz");
assertEquals(extractRequest.getBody(), tarGz);
} finally {
server.shutdown();
}
}
}

View File

@ -0,0 +1,197 @@
/*
* 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.openstack.swift.v1.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.openstack.swift.v1.options.UpdateContainerOptions;
import org.jclouds.openstack.swift.v1.reference.SwiftHeaders;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
/**
* Provides live tests for the {@link ContainerApi}.
*/
@Test(groups = "live", testName = "ContainerApiLiveTest")
public class ContainerApiLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
private String name = getClass().getSimpleName();
public void testCreateWithOptions() throws Exception {
for (String regionId : regions) {
ImmutableMultimap<String, String> headers =
ImmutableMultimap.of(SwiftHeaders.STATIC_WEB_INDEX, "__index.html",
SwiftHeaders.STATIC_WEB_ERROR, "__error.html");
CreateContainerOptions opts = new CreateContainerOptions().headers(headers);
assertNotNull(api.getContainerApi(regionId).create(name, opts));
Container container = api.getContainerApi(regionId).get(name);
assertNotNull(container);
assertEquals(container.getName(), name);
assertEquals(container.getMetadata().size(), 2);
assertEquals(container.getMetadata().get("web-index"), "__index.html");
assertEquals(container.getMetadata().get("web-error"), "__error.html");
assertTrue(api.getContainerApi(regionId).deleteIfEmpty(name));
}
}
public void testCreateWithSpacesAndSpecialCharacters() throws Exception {
final String nameWithSpaces = "container # ! special";
for (String regionId : regions) {
assertTrue(api.getContainerApi(regionId).create(nameWithSpaces));
Container container = api.getContainerApi(regionId).get(nameWithSpaces);
assertNotNull(container);
assertEquals(container.getName(), nameWithSpaces);
assertTrue(api.getContainerApi(regionId).deleteIfEmpty(nameWithSpaces));
}
}
public void testList() throws Exception {
for (String regionId : regions) {
ContainerApi containerApi = api.getContainerApi(regionId);
FluentIterable<Container> response = containerApi.list();
assertNotNull(response);
for (Container container : response) {
assertNotNull(container.getName());
assertTrue(container.getObjectCount() >= 0);
assertTrue(container.getBytesUsed() >= 0);
}
}
}
public void testListWithOptions() throws Exception {
String lexicographicallyBeforeName = name.substring(0, name.length() - 1);
for (String regionId : regions) {
ListContainerOptions options = ListContainerOptions.Builder.marker(lexicographicallyBeforeName);
Container container = api.getContainerApi(regionId).list(options).get(0);
assertEquals(container.getName(), name);
assertTrue(container.getObjectCount() == 0);
assertTrue(container.getBytesUsed() == 0);
}
}
public void testUpdate() throws Exception {
for (String regionId : regions) {
ImmutableMultimap<String, String> headers =
ImmutableMultimap.of(SwiftHeaders.STATIC_WEB_INDEX, "__index.html",
SwiftHeaders.STATIC_WEB_ERROR, "__error.html");
UpdateContainerOptions opts = new UpdateContainerOptions().headers(headers);
assertNotNull(api.getContainerApi(regionId).create(name));
Container container = api.getContainerApi(regionId).get(name);
assertNotNull(container);
assertEquals(container.getName(), name);
assertTrue(container.getMetadata().isEmpty());
assertNotNull(api.getContainerApi(regionId).update(name, opts));
Container updatedContainer = api.getContainerApi(regionId).get(name);
assertNotNull(updatedContainer);
assertEquals(updatedContainer.getName(), name);
assertEquals(updatedContainer.getMetadata().size(), 2);
assertEquals(updatedContainer.getMetadata().get("web-index"), "__index.html");
assertEquals(updatedContainer.getMetadata().get("web-error"), "__error.html");
assertTrue(api.getContainerApi(regionId).deleteIfEmpty(name));
}
}
public void testGet() throws Exception {
for (String regionId : regions) {
Container container = api.getContainerApi(regionId).get(name);
assertEquals(container.getName(), name);
assertTrue(container.getObjectCount() == 0);
assertTrue(container.getBytesUsed() == 0);
}
}
public void testUpdateMetadata() throws Exception {
Map<String, String> meta = ImmutableMap.of("MyAdd1", "foo", "MyAdd2", "bar");
for (String regionId : regions) {
ContainerApi containerApi = api.getContainerApi(regionId);
assertTrue(containerApi.updateMetadata(name, meta));
containerHasMetadata(containerApi, name, meta);
}
}
public void testDeleteMetadata() throws Exception {
Map<String, String> meta = ImmutableMap.of("MyDelete1", "foo", "MyDelete2", "bar");
for (String regionId : regions) {
ContainerApi containerApi = api.getContainerApi(regionId);
// update
assertTrue(containerApi.updateMetadata(name, meta));
containerHasMetadata(containerApi, name, meta);
// delete
assertTrue(containerApi.deleteMetadata(name, meta));
Container container = containerApi.get(name);
for (Entry<String, String> entry : meta.entrySet()) {
// note keys are returned in lower-case!
assertFalse(container.getMetadata().containsKey(entry.getKey().toLowerCase()));
}
}
}
static void containerHasMetadata(ContainerApi containerApi, String name, Map<String, String> meta) {
Container container = containerApi.get(name);
for (Entry<String, String> entry : meta.entrySet()) {
// note keys are returned in lower-case!
assertEquals(container.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue(),
container + " didn't have metadata: " + entry);
}
}
@Override
@BeforeClass(groups = "live")
public void setup() {
super.setup();
for (String regionId : regions) {
api.getContainerApi(regionId).create(name);
}
}
@Override
@AfterClass(groups = "live")
public void tearDown() {
for (String regionId : regions) {
api.getContainerApi(regionId).deleteIfEmpty(name);
}
super.tearDown();
}
}

View File

@ -0,0 +1,363 @@
/*
* 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.openstack.swift.v1.features;
import static org.jclouds.openstack.swift.v1.options.CreateContainerOptions.Builder.anybodyRead;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_BYTES_USED;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_OBJECT_COUNT;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_REMOVE_METADATA_PREFIX;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
@Test(groups = "unit", testName = "ContainerApiMockTest")
public class ContainerApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
public void testList() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/container_list.json"))));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
ImmutableList<Container> containers = api.getContainerApi("DFW").list().toList();
assertEquals(containers, ImmutableList.of(
Container.builder()
.name("test_container_1")
.objectCount(2)
.bytesUsed(78).build(),
Container.builder()
.name("test_container_2")
.objectCount(1)
.bytesUsed(17).build()));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "GET", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9");
} finally {
server.shutdown();
}
}
public void testListWithOptions() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/container_list.json"))));
ListContainerOptions options = ListContainerOptions.Builder.marker("test");
assertNotNull(options);
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
ImmutableList<Container> containers = api.getContainerApi("DFW").list(options).toList();
assertEquals(containers, ImmutableList.of(
Container.builder()
.name("test_container_1")
.objectCount(2)
.bytesUsed(78).build(),
Container.builder()
.name("test_container_2")
.objectCount(1)
.bytesUsed(17).build()));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "GET", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9?marker=test");
} finally {
server.shutdown();
}
}
public void testContainerExists() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201)));
server.enqueue(addCommonHeaders(containerResponse()
.addHeader(CONTAINER_METADATA_PREFIX + "ApiName", "swift")
.addHeader(CONTAINER_METADATA_PREFIX + "ApiVersion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").create("myContainer", anybodyRead().metadata(metadata)));
Container container = api.getContainerApi("DFW").get("myContainer");
assertEquals(container.getName(), "myContainer");
assertEquals(container.getObjectCount(), 42l);
assertEquals(container.getBytesUsed(), 323479l);
for (Entry<String, String> entry : container.getMetadata().entrySet()) {
assertEquals(container.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue());
}
assertEquals(server.getRequestCount(), 3);
assertAuthentication(server);
assertRequest(server.takeRequest(), "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
assertRequest(server.takeRequest(), "HEAD", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
} finally {
server.shutdown();
}
}
@Test(expectedExceptions = ContainerNotFoundException.class)
public void testContainerDoesNotExist() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").create("myContainer", anybodyRead().metadata(metadata)));
// the head call will throw the ContainerNotFoundException
api.getContainerApi("DFW").get("myContainer");
} finally {
server.shutdown();
}
}
public void testCreate() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").create("myContainer"));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
} finally {
server.shutdown();
}
}
public void testCreateWithOptions() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").create("myContainer", anybodyRead().metadata(metadata)));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
RecordedRequest createRequest = server.takeRequest();
assertRequest(createRequest, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
assertEquals(createRequest.getHeader(CONTAINER_READ), CONTAINER_ACL_ANYBODY_READ);
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(createRequest.getHeader(CONTAINER_METADATA_PREFIX + entry.getKey().toLowerCase()), entry.getValue());
}
} finally {
server.shutdown();
}
}
public void testCreateWithSpacesAndSpecialCharacters() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").create("container # ! special"));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/container%20%23%20%21%20special");
} finally {
server.shutdown();
}
}
public void testAlreadyCreated() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(202)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertFalse(api.getContainerApi("DFW").create("myContainer"));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
} finally {
server.shutdown();
}
}
/** upper-cases first char, and lower-cases rest!! **/
public void testGetKnowingServerMessesWithMetadataKeyCaseFormat() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(containerResponse()
// note silly casing
.addHeader(CONTAINER_METADATA_PREFIX + "Apiname", "swift")
.addHeader(CONTAINER_METADATA_PREFIX + "Apiversion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
Container container = api.getContainerApi("DFW").get("myContainer");
assertEquals(container.getName(), "myContainer");
assertEquals(container.getObjectCount(), 42l);
assertEquals(container.getBytesUsed(), 323479l);
for (Entry<String, String> entry : container.getMetadata().entrySet()) {
assertEquals(container.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue());
}
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "HEAD", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
} finally {
server.shutdown();
}
}
public void testUpdateMetadata() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(containerResponse()
.addHeader(CONTAINER_METADATA_PREFIX + "ApiName", "swift")
.addHeader(CONTAINER_METADATA_PREFIX + "ApiVersion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").updateMetadata("myContainer", metadata));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest replaceRequest = server.takeRequest();
assertEquals(replaceRequest.getRequestLine(),
"POST /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer HTTP/1.1");
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(replaceRequest.getHeader(CONTAINER_METADATA_PREFIX + entry.getKey().toLowerCase()), entry.getValue());
}
} finally {
server.shutdown();
}
}
public void testDeleteMetadata() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(containerResponse()));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").deleteMetadata("myContainer", metadata));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"POST /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer HTTP/1.1");
for (String key : metadata.keySet()) {
assertEquals(deleteRequest.getHeader(CONTAINER_REMOVE_METADATA_PREFIX + key.toLowerCase()), "ignored");
}
} finally {
server.shutdown();
}
}
public void testDeleteIfEmpty() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(204)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").deleteIfEmpty("myContainer"));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"DELETE /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer HTTP/1.1");
} finally {
server.shutdown();
}
}
public void testAlreadyDeleted() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getContainerApi("DFW").deleteIfEmpty("myContainer"));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"DELETE /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer HTTP/1.1");
} finally {
server.shutdown();
}
}
public void testDeleteWhenNotEmpty() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(409)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertFalse(api.getContainerApi("DFW").deleteIfEmpty("myContainer"));
} finally {
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"DELETE /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer HTTP/1.1");
server.shutdown();
}
}
private static final Map<String, String> metadata = ImmutableMap.of("ApiName", "swift", "ApiVersion", "v1.1");
static MockResponse containerResponse() {
return new MockResponse()
.addHeader(CONTAINER_OBJECT_COUNT, "42")
.addHeader(CONTAINER_BYTES_USED, "323479");
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.openstack.swift.v1.features;
import static org.jclouds.openstack.swift.v1.options.CreateContainerOptions.Builder.anybodyRead;
import static org.testng.Assert.assertTrue;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
@Test(groups = "live", testName = "CreatePublicContainerLiveTest")
public class CreatePublicContainerLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
private String name = getClass().getSimpleName();
public void testAnybodyReadUpdatesMetadata() throws Exception {
for (String regionId : api.getConfiguredRegions()) {
api.getContainerApi(regionId).create(name, anybodyRead());
assertTrue(api.getContainerApi(regionId).get(name).getAnybodyRead().get());
}
}
@Override
@AfterClass(groups = "live")
public void tearDown() {
for (String regionId : api.getConfiguredRegions()) {
api.getContainerApi(regionId).deleteIfEmpty(name);
}
super.tearDown();
}
}

View File

@ -0,0 +1,286 @@
/*
* 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.openstack.swift.v1.features;
import static org.jclouds.http.options.GetOptions.Builder.tail;
import static org.jclouds.io.Payloads.newByteSourcePayload;
import static org.jclouds.openstack.swift.v1.options.ListContainerOptions.Builder.marker;
import static org.jclouds.util.Strings2.toStringAndClose;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.jclouds.http.options.GetOptions;
import org.jclouds.io.Payload;
import org.jclouds.openstack.swift.v1.CopyObjectException;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteSource;
/**
* Provides live tests for the {@link ObjectApi}.
*/
@Test(groups = "live", testName = "ObjectApiLiveTest", singleThreaded = true)
public class ObjectApiLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
private String name = getClass().getSimpleName();
private String containerName = getClass().getSimpleName() + "Container";
static final Payload PAYLOAD = newByteSourcePayload(ByteSource.wrap("swifty".getBytes()));
public void testCreateWithSpacesAndSpecialCharacters() throws Exception {
final String containerName = "container # ! special";
final String objectName = "object # ! special";
for (String regionId : regions) {
assertNotNull(api.getContainerApi(regionId).create(containerName));
assertNotNull(api.getObjectApi(regionId, containerName).put(objectName, PAYLOAD));
SwiftObject object = api.getObjectApi(regionId, containerName).get(objectName);
assertEquals(object.getName(), objectName);
checkObject(object);
assertEquals(toStringAndClose(object.getPayload().openStream()), "swifty");
api.getObjectApi(regionId, containerName).delete(objectName);
api.getContainerApi(regionId).deleteIfEmpty(containerName);
}
}
public void testPutWithExpiration() throws Exception {
String objectName = "test-expiration";
long expireMillis = new Date().getTime() + 1000 * 60 * 60 * 24;
Date expireAt = new Date(expireMillis);
Payload payload = newByteSourcePayload(ByteSource.wrap("swifty".getBytes()));
payload.getContentMetadata().setExpires(expireAt);
for (String regionId : regions) {
String etag = api.getObjectApi(regionId, containerName).put(objectName, payload);
assertNotNull(etag);
SwiftObject object = api.getObjectApi(regionId, containerName).get(objectName);
assertEquals(object.getName(), objectName);
checkObject(object);
assertEquals(toStringAndClose(object.getPayload().openStream()), "swifty");
api.getObjectApi(regionId, containerName).delete(objectName);
}
}
public void testCopyObject() throws Exception {
for (String regionId : regions) {
// source
String sourceContainer = "src" + containerName;
String sourceObjectName = "original.txt";
String badSource = "badSource";
// destination
String destinationContainer = "dest" + containerName;
String destinationObject = "copy.txt";
String destinationPath = "/" + destinationContainer + "/" + destinationObject;
ContainerApi containerApi = api.getContainerApi(regionId);
// create source and destination dirs
containerApi.create(sourceContainer);
containerApi.create(destinationContainer);
// get the api for this region and container
ObjectApi srcApi = api.getObjectApi(regionId, sourceContainer);
ObjectApi destApi = api.getObjectApi(regionId, destinationContainer);
// Create source object
assertNotNull(srcApi.put(sourceObjectName, PAYLOAD));
SwiftObject sourceObject = srcApi.get(sourceObjectName);
checkObject(sourceObject);
// Create the destination object
assertNotNull(destApi.put(destinationObject, PAYLOAD));
SwiftObject object = destApi.get(destinationObject);
checkObject(object);
// check the copy operation
assertTrue(destApi.copy(destinationObject, sourceContainer, sourceObjectName));
assertNotNull(destApi.get(destinationObject));
// now get a real SwiftObject
SwiftObject destSwiftObject = destApi.get(destinationObject);
assertEquals(toStringAndClose(destSwiftObject.getPayload().openStream()), "swifty");
// test exception thrown on bad source name
try {
destApi.copy(destinationObject, badSource, sourceObjectName);
fail("Expected CopyObjectException");
} catch (CopyObjectException e) {
assertEquals(e.getSourcePath(), "/" + badSource + "/" + sourceObjectName);
assertEquals(e.getDestinationPath(), destinationPath);
}
deleteAllObjectsInContainer(regionId, sourceContainer);
containerApi.deleteIfEmpty(sourceContainer);
deleteAllObjectsInContainer(regionId, destinationContainer);
containerApi.deleteIfEmpty(destinationContainer);
}
}
public void testList() throws Exception {
for (String regionId : regions) {
ObjectApi objectApi = api.getObjectApi(regionId, containerName);
ObjectList response = objectApi.list();
assertEquals(response.getContainer(), api.getContainerApi(regionId).get(containerName));
for (SwiftObject object : response) {
checkObject(object);
}
}
}
public void testListWithOptions() throws Exception {
for (String regionId : regions) {
ObjectApi objectApi = api.getObjectApi(regionId, containerName);
ObjectList response = objectApi.list(ListContainerOptions.NONE);
assertEquals(response.getContainer(), api.getContainerApi(regionId).get(containerName));
for (SwiftObject object : response) {
checkObject(object);
}
}
}
public void testMetadata() throws Exception {
for (String regionId : regions) {
SwiftObject object = api.getObjectApi(regionId, containerName).get(name);
assertEquals(object.getName(), name);
checkObject(object);
assertEquals(toStringAndClose(object.getPayload().openStream()), "swifty");
}
}
public void testUpdateMetadata() throws Exception {
for (String regionId : regions) {
ObjectApi objectApi = api.getObjectApi(regionId, containerName);
Map<String, String> meta = ImmutableMap.of("MyAdd1", "foo", "MyAdd2", "bar");
assertTrue(objectApi.updateMetadata(name, meta));
SwiftObject object = objectApi.get(name);
for (Entry<String, String> entry : meta.entrySet()) {
// note keys are returned in lower-case!
assertEquals(object.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue(),
object + " didn't have metadata: " + entry);
}
}
}
public void testGet() throws Exception {
for (String regionId : regions) {
SwiftObject object = api.getObjectApi(regionId, containerName).get(name, GetOptions.NONE);
assertEquals(object.getName(), name);
checkObject(object);
assertEquals(toStringAndClose(object.getPayload().openStream()), "swifty");
}
}
public void testPrivateByDefault() throws Exception {
for (String regionId : regions) {
SwiftObject object = api.getObjectApi(regionId, containerName).get(name);
try {
object.getUri().toURL().openStream();
fail("shouldn't be able to access " + object);
} catch (IOException expected) {
}
}
}
public void testGetOptions() throws Exception {
for (String regionId : regions) {
SwiftObject object = api.getObjectApi(regionId, containerName).get(name, tail(1));
assertEquals(object.getName(), name);
checkObject(object);
assertEquals(toStringAndClose(object.getPayload().openStream()), "y");
}
}
public void testListOptions() throws Exception {
String lexicographicallyBeforeName = name.substring(0, name.length() - 1);
for (String regionId : regions) {
SwiftObject object = api.getObjectApi(regionId, containerName)
.list(marker(lexicographicallyBeforeName)).get(0);
assertEquals(object.getName(), name);
checkObject(object);
}
}
public void testDeleteMetadata() throws Exception {
for (String regionId : regions) {
ObjectApi objectApi = api.getObjectApi(regionId, containerName);
Map<String, String> meta = ImmutableMap.of("MyDelete1", "foo", "MyDelete2", "bar");
assertTrue(objectApi.updateMetadata(name, meta));
assertFalse(objectApi.get(name).getMetadata().isEmpty());
assertTrue(objectApi.deleteMetadata(name, meta));
assertTrue(objectApi.get(name).getMetadata().isEmpty());
}
}
@Override
@BeforeClass(groups = "live")
public void setup() {
super.setup();
for (String regionId : regions) {
api.getContainerApi(regionId).create(containerName);
api.getObjectApi(regionId, containerName).put(name, PAYLOAD);
}
}
@Override
@AfterClass(groups = "live")
public void tearDown() {
for (String regionId : regions) {
deleteAllObjectsInContainer(regionId, containerName);
api.getObjectApi(regionId, containerName).delete(name);
api.getContainerApi(regionId).deleteIfEmpty(containerName);
}
super.tearDown();
}
static void checkObject(SwiftObject object) {
assertNotNull(object.getName());
assertNotNull(object.getUri());
assertNotNull(object.getETag());
assertTrue(object.getLastModified().getTime() <= System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5));
assertNotNull(object.getPayload().getContentMetadata().getContentLength());
assertNotNull(object.getPayload().getContentMetadata().getContentType());
}
}

View File

@ -0,0 +1,515 @@
/*
* 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.openstack.swift.v1.features;
import static com.google.common.base.Charsets.US_ASCII;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.net.HttpHeaders.EXPIRES;
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
import static org.jclouds.Constants.PROPERTY_RETRY_DELAY_START;
import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
import static org.jclouds.http.options.GetOptions.Builder.tail;
import static org.jclouds.io.Payloads.newByteSourcePayload;
import static org.jclouds.openstack.swift.v1.features.ContainerApiMockTest.containerResponse;
import static org.jclouds.openstack.swift.v1.options.ListContainerOptions.Builder.marker;
import static org.jclouds.openstack.swift.v1.options.PutOptions.Builder.metadata;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_REMOVE_METADATA_PREFIX;
import static org.jclouds.util.Strings2.toStringAndClose;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.io.IOException;
import java.net.URI;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.http.HttpResponseException;
import org.jclouds.io.Payload;
import org.jclouds.io.payloads.ByteSourcePayload;
import org.jclouds.openstack.swift.v1.CopyObjectException;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.openstack.swift.v1.reference.SwiftHeaders;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteSource;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
/**
* Provides mock tests for the {@link ObjectApi}.
*/
@Test(groups = "unit", testName = "ObjectApiMockTest")
public class ObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
SimpleDateFormatDateService dates = new SimpleDateFormatDateService();
static final Payload PAYLOAD = newByteSourcePayload(ByteSource.wrap("swifty".getBytes()));
protected ImmutableList<SwiftObject> parsedObjectsForUrl(String baseUri) {
baseUri += "v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer";
return ImmutableList.of(
SwiftObject.builder()
.name("test_obj_1")
.uri(URI.create(baseUri + "/test_obj_1"))
.etag("4281c348eaf83e70ddce0e07221c3d28")
.payload(payload(14, "application/octet-stream", new Date(1406243553)))
.lastModified(dates.iso8601DateParse("2009-02-03T05:26:32.612278")).build(),
SwiftObject.builder()
.name("test_obj_2")
.uri(URI.create(baseUri + "/test_obj_2"))
.etag("b039efe731ad111bc1b0ef221c3849d0")
.payload(payload(64l, "application/octet-stream", null))
.lastModified(dates.iso8601DateParse("2009-02-03T05:26:32.612278")).build(),
SwiftObject.builder()
.name("test obj 3")
.uri(URI.create(baseUri + "/test%20obj%203"))
.etag("0b2e80bd0744d9ebb20484149a57c82e")
.payload(payload(14, "application/octet-stream", new Date()))
.lastModified(dates.iso8601DateParse("2014-05-20T05:26:32.612278")).build());
}
public void testList() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(containerResponse()
.addHeader(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ)
.setBody(stringFromResource("/object_list.json"))));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
ObjectList objects = api.getObjectApi("DFW", "myContainer").list();
assertEquals(objects, parsedObjectsForUrl(server.getUrl("/").toString()));
assertEquals(objects.getContainer().getName(), "myContainer");
assertTrue(objects.getContainer().getAnybodyRead().get());
// Check MD5 is parsed from the ETag header.
SwiftObject object1 = objects.get(1);
assertEquals(base16().lowerCase().decode(object1.getETag()),
object1.getPayload().getContentMetadata().getContentMD5AsHashCode().asBytes());
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "GET", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
} finally {
server.shutdown();
}
}
public void testListWithOptions() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(containerResponse()
.addHeader(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ)
.setBody(stringFromResource("/object_list.json"))));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
ObjectList objects = api.getObjectApi("DFW", "myContainer").list(new ListContainerOptions());
assertEquals(objects, parsedObjectsForUrl(server.getUrl("/").toString()));
assertEquals(objects.getContainer().getName(), "myContainer");
assertTrue(objects.getContainer().getAnybodyRead().get());
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "GET", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer");
} finally {
server.shutdown();
}
}
public void testListOptions() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(containerResponse().setBody(stringFromResource("/object_list.json"))));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
ObjectList objects = api.getObjectApi("DFW", "myContainer").list(marker("test"));
assertEquals(objects, parsedObjectsForUrl(server.getUrl("/").toString()));
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "GET", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer?marker=test");
} finally {
server.shutdown();
}
}
public void testCreate() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse()
.setResponseCode(201)
.addHeader("ETag", "d9f5eb4bba4e2f2f046e54611bc8196b"))
.addHeader("Expires", "1406243553"));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertEquals(
api.getObjectApi("DFW", "myContainer").put("myObject", PAYLOAD,
metadata(metadata)), "d9f5eb4bba4e2f2f046e54611bc8196b");
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
RecordedRequest replace = server.takeRequest();
assertRequest(replace, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject");
assertEquals(new String(replace.getBody()), "swifty");
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(replace.getHeader(OBJECT_METADATA_PREFIX + entry.getKey().toLowerCase()), entry.getValue());
}
} finally {
server.shutdown();
}
}
public void testCreateWithSpacesAndSpecialCharacters() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201).addHeader("ETag", "d9f5eb4bba4e2f2f046e54611bc8196b")));
final String containerName = "container # ! special";
final String objectName = "object # ! special";
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertEquals(
api.getObjectApi("DFW", containerName).put(objectName, PAYLOAD,
metadata(metadata)), "d9f5eb4bba4e2f2f046e54611bc8196b");
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
RecordedRequest replace = server.takeRequest();
assertRequest(replace, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/container%20%23%20%21%20special/object%20%23%20%21%20special");
assertEquals(new String(replace.getBody()), "swifty");
} finally {
server.shutdown();
}
}
public void testCreateWith408Retry() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(408))); // 1
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(408))); // 2
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(408))); // 3
// Finally success
server.enqueue(addCommonHeaders(new MockResponse()
.setResponseCode(201)
.addHeader("ETag", "d9f5eb4bba4e2f2f046e54611bc8196b")));
try {
Properties overrides = new Properties();
overrides.setProperty(PROPERTY_MAX_RETRIES, 5 + "");
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift", overrides);
assertEquals(
api.getObjectApi("DFW", "myContainer").put("myObject", PAYLOAD,
metadata(metadata)), "d9f5eb4bba4e2f2f046e54611bc8196b");
assertEquals(server.getRequestCount(), 5);
assertAuthentication(server);
RecordedRequest replace = server.takeRequest();
// This should take a while.
assertRequest(replace, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject");
assertEquals(new String(replace.getBody()), "swifty");
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(replace.getHeader(OBJECT_METADATA_PREFIX + entry.getKey().toLowerCase()), entry.getValue());
}
} finally {
server.shutdown();
}
}
/** upper-cases first char, and lower-cases rest!! **/
public void testGetWithoutKnowingServerMessesWithMetadataKeyCaseFormat() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(objectResponse()
// note silly casing
.addHeader(OBJECT_METADATA_PREFIX + "Apiname", "swift")
.addHeader(OBJECT_METADATA_PREFIX + "Apiversion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
SwiftObject object = api.getObjectApi("DFW", "myContainer").getWithoutBody("myObject");
assertEquals(object.getName(), "myObject");
assertEquals(object.getETag(), "8a964ee2a5e88be344f36c22562a6486");
// Check MD5 is parsed from the ETag header.
assertEquals(base16().lowerCase().decode(object.getETag()),
object.getPayload().getContentMetadata().getContentMD5AsHashCode().asBytes());
assertEquals(object.getLastModified(), dates.rfc822DateParse("Fri, 12 Jun 2010 13:40:18 GMT"));
for (Entry<String, String> entry : object.getMetadata().entrySet()) {
assertEquals(object.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue());
}
assertEquals(object.getPayload().getContentMetadata().getContentType(), "text/plain; charset=UTF-8");
assertEquals(toStringAndClose(object.getPayload().openStream()), "");
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "HEAD", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject");
} finally {
server.shutdown();
}
}
public void testGet() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(objectResponse()
// note silly casing
.addHeader(OBJECT_METADATA_PREFIX + "Apiname", "swift")
.addHeader(OBJECT_METADATA_PREFIX + "Apiversion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
SwiftObject object = api.getObjectApi("DFW", "myContainer").get("myObject", tail(1));
assertEquals(object.getName(), "myObject");
assertEquals(object.getETag(), "8a964ee2a5e88be344f36c22562a6486");
assertEquals(object.getLastModified(), dates.rfc822DateParse("Fri, 12 Jun 2010 13:40:18 GMT"));
for (Entry<String, String> entry : object.getMetadata().entrySet()) {
assertEquals(object.getMetadata().get(entry.getKey().toLowerCase()), entry.getValue());
}
Payload payload = object.getPayload();
assertEquals(payload.getContentMetadata().getContentLength(), Long.valueOf(4));
assertEquals(payload.getContentMetadata().getContentType(), "text/plain; charset=UTF-8");
assertEquals(payload.getContentMetadata().getExpires(), dates.rfc822DateParse("Wed, 23 Jul 2014 14:00:00 GMT"));
assertEquals(toStringAndClose(payload.openStream()), "ABCD");
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest get = server.takeRequest();
assertEquals(get.getRequestLine(),
"GET /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject HTTP/1.1");
} finally {
server.shutdown();
}
}
@Test(expectedExceptions = HttpResponseException.class, timeOut = 20000)
public void testCreateWithTimeout() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
// Typically we would enqueue a response for the put. However, in this case, test the timeout by not providing one.
try {
Properties overrides = new Properties();
overrides.setProperty(PROPERTY_SO_TIMEOUT, 5000 + ""); // This time-outs the connection
overrides.setProperty(PROPERTY_MAX_RETRIES, 0 + ""); // 0 retries == 1 try. Semantics.
overrides.setProperty(PROPERTY_RETRY_DELAY_START, 0 + ""); // exponential backoff already working for this call. This is the delay BETWEEN attempts.
final SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift", overrides);
api.getObjectApi("DFW", "myContainer").put("myObject", new ByteSourcePayload(ByteSource.wrap("swifty".getBytes())), metadata(metadata));
fail("testReplaceTimeout test should have failed with an HttpResponseException.");
} finally {
server.shutdown();
}
}
public void testUpdateMetadata() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(objectResponse()
.addHeader(OBJECT_METADATA_PREFIX + "ApiName", "swift")
.addHeader(OBJECT_METADATA_PREFIX + "ApiVersion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getObjectApi("DFW", "myContainer").updateMetadata("myObject", metadata));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest replaceRequest = server.takeRequest();
assertEquals(replaceRequest.getRequestLine(),
"POST /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject HTTP/1.1");
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(replaceRequest.getHeader(OBJECT_METADATA_PREFIX + entry.getKey().toLowerCase()), entry.getValue());
}
} finally {
server.shutdown();
}
}
public void testUpdateMetadataContentType() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(objectResponse()
.addHeader(OBJECT_METADATA_PREFIX + "ApiName", "swift")
.addHeader(OBJECT_METADATA_PREFIX + "ApiVersion", "v1.1")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getObjectApi("DFW", "myContainer").updateMetadata("myObject", metadata));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest replaceRequest = server.takeRequest();
assertEquals(replaceRequest.getHeaders("Content-Type").get(0), "", "updateMetadata should send an empty content-type header, but sent "
+ replaceRequest.getHeaders("Content-Type").get(0).toString());
assertEquals(replaceRequest.getRequestLine(),
"POST /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject HTTP/1.1");
for (Entry<String, String> entry : metadata.entrySet()) {
assertEquals(replaceRequest.getHeader(OBJECT_METADATA_PREFIX + entry.getKey().toLowerCase()), entry.getValue());
}
} finally {
server.shutdown();
}
}
public void testDeleteMetadata() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(objectResponse()));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getObjectApi("DFW", "myContainer").deleteMetadata("myObject", metadata));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"POST /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject HTTP/1.1");
for (String key : metadata.keySet()) {
assertEquals(deleteRequest.getHeader(OBJECT_REMOVE_METADATA_PREFIX + key.toLowerCase()), "ignored");
}
} finally {
server.shutdown();
}
}
public void testDelete() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(204)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
api.getObjectApi("DFW", "myContainer").delete("myObject");
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"DELETE /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject HTTP/1.1");
} finally {
server.shutdown();
}
}
public void testAlreadyDeleted() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
api.getObjectApi("DFW", "myContainer").delete("myObject");
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest deleteRequest = server.takeRequest();
assertEquals(deleteRequest.getRequestLine(),
"DELETE /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject HTTP/1.1");
} finally {
server.shutdown();
}
}
public void testCopyObject() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201)
.addHeader(SwiftHeaders.OBJECT_COPY_FROM, "/bar/foo.txt")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertTrue(api.getObjectApi("DFW", "foo")
.copy("bar.txt", "bar", "foo.txt"));
assertEquals(server.getRequestCount(), 2);
assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
RecordedRequest copyRequest = server.takeRequest();
assertEquals(copyRequest.getRequestLine(),
"PUT /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/foo/bar.txt HTTP/1.1");
} finally {
server.shutdown();
}
}
@Test(expectedExceptions = CopyObjectException.class)
public void testCopyObjectFail() throws InterruptedException, IOException {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)
.addHeader(SwiftHeaders.OBJECT_COPY_FROM, "/bogus/foo.txt")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
// the following line will throw the CopyObjectException
api.getObjectApi("DFW", "foo").copy("bar.txt", "bogus", "foo.txt");
} finally {
server.shutdown();
}
}
private static final Map<String, String> metadata = ImmutableMap.of("ApiName", "swift", "ApiVersion", "v1.1");
static MockResponse objectResponse() {
return new MockResponse()
.addHeader("Last-Modified", "Fri, 12 Jun 2010 13:40:18 GMT")
.addHeader("ETag", "8a964ee2a5e88be344f36c22562a6486")
// TODO: MWS doesn't allow you to return content length w/o content
// on HEAD!
.setBody("ABCD".getBytes(US_ASCII))
.addHeader("Content-Length", "4")
.addHeader("Content-Type", "text/plain; charset=UTF-8")
.addHeader(EXPIRES, "Wed, 23 Jul 2014 14:00:00 GMT");
}
static Payload payload(long bytes, String contentType, Date expires) {
Payload payload = newByteSourcePayload(ByteSource.empty());
payload.getContentMetadata().setContentLength(bytes);
payload.getContentMetadata().setContentType(contentType);
payload.getContentMetadata().setExpires(expires);
return payload;
}
}

View File

@ -0,0 +1,129 @@
/*
* 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.openstack.swift.v1.features;
import static java.lang.String.format;
import static org.jclouds.io.Payloads.newByteSourcePayload;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.Segment;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteSource;
@Test(groups = "live", testName = "StaticLargeObjectApiLiveTest")
public class StaticLargeObjectApiLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
private String name = getClass().getSimpleName();
private String containerName = getClass().getSimpleName() + "Container";
private byte[] megOf1s;
private byte[] megOf2s;
public void testNotPresentWhenDeleting() throws Exception {
for (String regionId : regions) {
api.getStaticLargeObjectApi(regionId, containerName).delete(UUID.randomUUID().toString());
}
}
public void testReplaceManifest() throws Exception {
for (String regionId : regions) {
ObjectApi objectApi = api.getObjectApi(regionId, containerName);
String etag1s = objectApi.put(name + "/1", newByteSourcePayload(ByteSource.wrap(megOf1s)));
assertMegabyteAndETagMatches(regionId, name + "/1", etag1s);
String etag2s = objectApi.put(name + "/2", newByteSourcePayload(ByteSource.wrap(megOf2s)));
assertMegabyteAndETagMatches(regionId, name + "/2", etag2s);
List<Segment> segments = ImmutableList.<Segment> builder()
.add(Segment.builder()
.path(format("%s/%s/1", containerName, name)).etag(etag1s).sizeBytes(1024 * 1024)
.build())
.add(Segment.builder()
.path(format("%s/%s/2", containerName, name)).etag(etag2s).sizeBytes(1024 * 1024)
.build())
.build();
String etagOfEtags = api.getStaticLargeObjectApi(regionId, containerName).replaceManifest(
name, segments, ImmutableMap.of("myfoo", "Bar"));
assertNotNull(etagOfEtags);
SwiftObject bigObject = api.getObjectApi(regionId, containerName).get(name);
assertEquals(bigObject.getETag(), etagOfEtags);
assertEquals(bigObject.getPayload().getContentMetadata().getContentLength(), Long.valueOf(2 * 1024 * 1024));
assertEquals(bigObject.getMetadata(), ImmutableMap.of("myfoo", "Bar"));
// segments are visible
assertEquals(api.getContainerApi(regionId).get(containerName).getObjectCount(), 3);
}
}
@Test(dependsOnMethods = "testReplaceManifest")
public void testDelete() throws Exception {
for (String regionId : regions) {
api.getStaticLargeObjectApi(regionId, containerName).delete(name);
assertEquals(api.getContainerApi(regionId).get(containerName).getObjectCount(), 0);
}
}
protected void assertMegabyteAndETagMatches(String regionId, String name, String etag1s) {
SwiftObject object1s = api.getObjectApi(regionId, containerName).get(name);
assertEquals(object1s.getETag(), etag1s);
assertEquals(object1s.getPayload().getContentMetadata().getContentLength(), Long.valueOf(1024 * 1024));
}
@Override
@BeforeClass(groups = "live")
public void setup() {
super.setup();
for (String regionId : regions) {
boolean created = api.getContainerApi(regionId).create(containerName);
if (!created) {
deleteAllObjectsInContainer(regionId, containerName);
}
}
megOf1s = new byte[1024 * 1024];
megOf2s = new byte[1024 * 1024];
Arrays.fill(megOf1s, (byte) 1);
Arrays.fill(megOf2s, (byte) 2);
}
@Override
@AfterClass(groups = "live")
public void tearDown() {
for (String regionId : regions) {
deleteAllObjectsInContainer(regionId, containerName);
api.getContainerApi(regionId).deleteIfEmpty(containerName);
}
super.tearDown();
}
}

View File

@ -0,0 +1,107 @@
/*
* 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.openstack.swift.v1.features;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_METADATA_PREFIX;
import static org.testng.Assert.assertEquals;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.Segment;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HttpHeaders;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
@Test(groups = "unit", testName = "StaticLargeObjectApiMockTest")
public class StaticLargeObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
public void testReplaceManifest() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().addHeader(HttpHeaders.ETAG, "\"abcd\"")));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
assertEquals(
api.getStaticLargeObjectApi("DFW", "myContainer").replaceManifest(
"myObject",
ImmutableList
.<Segment> builder()
.add(Segment.builder().path("/mycontainer/objseg1").etag("0228c7926b8b642dfb29554cd1f00963")
.sizeBytes(1468006).build())
.add(Segment.builder().path("/mycontainer/pseudodir/seg-obj2")
.etag("5bfc9ea51a00b790717eeb934fb77b9b").sizeBytes(1572864).build())
.add(Segment.builder().path("/other-container/seg-final")
.etag("b9c3da507d2557c1ddc51f27c54bae51").sizeBytes(256).build()).build(),
ImmutableMap.of("MyFoo", "Bar")), "abcd");
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
RecordedRequest replaceRequest = server.takeRequest();
assertRequest(replaceRequest, "PUT", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=put");
assertEquals(replaceRequest.getHeader(OBJECT_METADATA_PREFIX + "myfoo"), "Bar");
assertEquals(
new String(replaceRequest.getBody()),
"[{\"path\":\"/mycontainer/objseg1\",\"etag\":\"0228c7926b8b642dfb29554cd1f00963\",\"size_bytes\":1468006}," +
"{\"path\":\"/mycontainer/pseudodir/seg-obj2\",\"etag\":\"5bfc9ea51a00b790717eeb934fb77b9b\",\"size_bytes\":1572864}," +
"{\"path\":\"/other-container/seg-final\",\"etag\":\"b9c3da507d2557c1ddc51f27c54bae51\",\"size_bytes\":256}]");
} finally {
server.shutdown();
}
}
public void testDelete() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(204)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
api.getStaticLargeObjectApi("DFW", "myContainer").delete("myObject");
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "DELETE", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=delete");
} finally {
server.shutdown();
}
}
public void testAlreadyDeleted() throws Exception {
MockWebServer server = mockOpenStackServer();
server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(404)));
try {
SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
api.getStaticLargeObjectApi("DFW", "myContainer").delete("myObject");
assertEquals(server.getRequestCount(), 2);
assertAuthentication(server);
assertRequest(server.takeRequest(), "DELETE", "/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=delete");
} finally {
server.shutdown();
}
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.openstack.swift.v1.features;
import static org.testng.Assert.assertEquals;
import org.jclouds.http.HttpRequest;
import org.jclouds.openstack.swift.v1.features.BulkApi.UrlEncodeAndJoinOnNewline;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
@Test(groups = "unit", testName = "UrlEncodeAndJoinOnNewlineTest")
public class UrlEncodeAndJoinOnNewlineTest {
UrlEncodeAndJoinOnNewline binder = new UrlEncodeAndJoinOnNewline();
public void urlEncodesPaths() {
HttpRequest request = HttpRequest.builder()
.method("DELETE")
.endpoint("https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_XXXXXX/")
.addQueryParam("bulk-delete").build();
request = binder.bindToRequest(request, ImmutableList.<String> builder()
.add("/v1/12345678912345/mycontainer/home/xx<yy")
.add("/v1/12345678912345/mycontainer/../image.gif").build());
assertEquals(request.getPayload().getRawContent(), "/v1/12345678912345/mycontainer/home/xx%3Cyy\n"
+ "/v1/12345678912345/mycontainer/../image.gif");
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.openstack.swift.v1.internal;
import static com.google.common.base.Preconditions.checkState;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.apis.BaseApiLiveTest;
import org.jclouds.location.reference.LocationConstants;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Uninterruptibles;
@Test(groups = "live", testName = "BaseSwiftApiLiveTest")
public abstract class BaseSwiftApiLiveTest<A extends SwiftApi> extends BaseApiLiveTest<A> {
protected Set<String> regions;
protected BaseSwiftApiLiveTest() {
provider = "openstack-swift";
}
@Override
@BeforeClass(groups = "live")
public void setup() {
super.setup();
String providedRegion = System.getProperty("test." + LocationConstants.PROPERTY_REGION);
if (providedRegion != null) {
regions = ImmutableSet.of(providedRegion);
} else {
regions = api.getConfiguredRegions();
}
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
setIfTestSystemPropertyPresent(props, KeystoneProperties.CREDENTIAL_TYPE);
setIfTestSystemPropertyPresent(props, LocationConstants.PROPERTY_REGION);
return props;
}
protected void deleteAllObjectsInContainer(String regionId, final String containerName) {
Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
ObjectList objects = api.getObjectApi(regionId, containerName).list(new ListContainerOptions());
if (objects == null) {
return;
}
List<String> pathsToDelete = Lists.transform(objects, new Function<SwiftObject, String>() {
public String apply(SwiftObject input) {
return containerName + "/" + input.getName();
}
});
if (!pathsToDelete.isEmpty()) {
BulkDeleteResponse response = api.getBulkApi(regionId).bulkDelete(pathsToDelete);
checkState(response.getErrors().isEmpty(), "Errors deleting paths %s: %s", pathsToDelete, response);
}
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.openstack.swift.v1.options;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_QUOTA_BYTES;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
/**
* Tests behavior of {@link CreateContainerOptions}.
*/
@Test(groups = "unit")
public class CreateContainerOptionsTest {
public void testMetadata() {
CreateContainerOptions options =
new CreateContainerOptions().metadata(ImmutableMap.of("ApiName", "swift", "metaKey2", "Value2", "METAKEY3", "VALUE 3 "));
Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey2"), ImmutableList.of("Value2"));
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey3"), ImmutableList.of("VALUE 3 "));
}
public void testHeaders() {
CreateContainerOptions options =
new CreateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_QUOTA_BYTES, "5120", CONTAINER_METADATA_PREFIX + "apiname", "swift"));
Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get(CONTAINER_QUOTA_BYTES), ImmutableList.of("5120"));
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
}
public void testAnybodyRead() {
CreateContainerOptions options =
new CreateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ));
assertEquals(options.buildRequestHeaders().get(CONTAINER_READ), ImmutableList.of(CONTAINER_ACL_ANYBODY_READ));
}
public void testVersionsLocation() {
CreateContainerOptions options =
new CreateContainerOptions().headers(ImmutableMultimap.of(VERSIONS_LOCATION, "containerWithVersions"));
assertEquals(options.buildRequestHeaders().get(VERSIONS_LOCATION), ImmutableList.of("containerWithVersions"));
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.openstack.swift.v1.options;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.OBJECT_DELETE_AT;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
/**
* Tests behavior of {@link PutOptions}.
*/
@Test(groups = "unit")
public class PutOptionsTest {
public void testPutMetadata() {
PutOptions options =
new PutOptions().metadata(ImmutableMap.of("ApiName", "swift"));
assertEquals(options.buildRequestHeaders().get(OBJECT_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
}
public void testPutHeaders() {
PutOptions options =
new PutOptions().headers(ImmutableMultimap.of(OBJECT_DELETE_AT, "123456789"));
assertEquals(options.buildRequestHeaders().get(OBJECT_DELETE_AT), ImmutableList.of("123456789"));
}
}

View File

@ -0,0 +1,122 @@
/*
* 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.openstack.swift.v1.options;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_ACL_ANYBODY_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_METADATA_PREFIX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_QUOTA_BYTES;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.CONTAINER_READ;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.VERSIONS_LOCATION;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_DIRECTORY_TYPE;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_ERROR;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_INDEX;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS;
import static org.jclouds.openstack.swift.v1.reference.SwiftHeaders.STATIC_WEB_LISTINGS_CSS;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.common.net.MediaType;
/**
* Tests behavior of {@link UpdateContainerOptions}.
*/
@Test(groups = "unit")
public class UpdateContainerOptionsTest {
public void testAnybodyRead() {
UpdateContainerOptions options = new UpdateContainerOptions().anybodyRead();
assertEquals(options.buildRequestHeaders().get(CONTAINER_READ), ImmutableList.of(CONTAINER_ACL_ANYBODY_READ));
}
public void testAnybodyReadViaHeaders() {
UpdateContainerOptions options =
new UpdateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_READ, CONTAINER_ACL_ANYBODY_READ));
assertEquals(options.buildRequestHeaders().get(CONTAINER_READ), ImmutableList.of(CONTAINER_ACL_ANYBODY_READ));
}
public void testVersionsLocation() {
UpdateContainerOptions options = new UpdateContainerOptions().versionsLocation("containerWithVersions");
assertEquals(options.buildRequestHeaders().get(VERSIONS_LOCATION), ImmutableList.of("containerWithVersions"));
}
public void testVersionsLocationViaHeaders() {
UpdateContainerOptions options =
new UpdateContainerOptions().headers(ImmutableMultimap.of(VERSIONS_LOCATION, "containerWithVersions"));
assertEquals(options.buildRequestHeaders().get(VERSIONS_LOCATION), ImmutableList.of("containerWithVersions"));
}
public void testMetadata() {
UpdateContainerOptions options =
new UpdateContainerOptions().metadata(ImmutableMap.of("ApiName", "swift", "metaKey2", "Value2", "METAKEY3", "VALUE 3 "));
Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey2"), ImmutableList.of("Value2"));
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "metakey3"), ImmutableList.of("VALUE 3 "));
}
public void testHeaders() {
UpdateContainerOptions options =
new UpdateContainerOptions().headers(ImmutableMultimap.of(CONTAINER_QUOTA_BYTES, "5120", CONTAINER_METADATA_PREFIX + "apiname", "swift"));
Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get(CONTAINER_QUOTA_BYTES), ImmutableList.of("5120"));
assertEquals(headers.get(CONTAINER_METADATA_PREFIX + "apiname"), ImmutableList.of("swift"));
}
public void testStaticWebsiteDirectoryType() {
MediaType appDir = MediaType.create("application", "directory");
Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_DIRECTORY_TYPE, appDir.toString());
UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
assertEquals(options.buildRequestHeaders().get(STATIC_WEB_DIRECTORY_TYPE), ImmutableList.of(appDir.toString()));
}
public void testStaticWebsiteIndexPage() {
Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_INDEX, "index.html");
UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
assertEquals(options.buildRequestHeaders().get(STATIC_WEB_INDEX), ImmutableList.of("index.html"));
}
public void testStaticWebsiteErrorPage() {
Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_ERROR, "error.html");
UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
assertEquals(options.buildRequestHeaders().get(STATIC_WEB_ERROR), ImmutableList.of("error.html"));
}
public void testEnableStaticWebsiteListings() {
Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_LISTINGS, "true");
UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
assertEquals(options.buildRequestHeaders().get(STATIC_WEB_LISTINGS), ImmutableList.of("true"));
}
public void testDiableStaticWebsiteListings() {
Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_LISTINGS, "false");
UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
assertEquals(options.buildRequestHeaders().get(STATIC_WEB_LISTINGS), ImmutableList.of("false"));
}
public void testStaticWebsiteListingsCSS() {
Multimap<String, String> headers = ImmutableMultimap.of(STATIC_WEB_LISTINGS_CSS, "listings.css");
UpdateContainerOptions options = new UpdateContainerOptions().headers(headers);
assertEquals(options.buildRequestHeaders().get(STATIC_WEB_LISTINGS_CSS), ImmutableList.of("listings.css"));
}
}

View File

@ -0,0 +1,249 @@
{
"access":{
"token":{
"id":"bb03a23aa8271291a7aaa9aaa2aaaaaa",
"expires":"2013-08-02T16:55:24.229-05:00",
"tenant":{
"id":"888888",
"name":"888888"
},
"RAX-AUTH:authenticatedBy":[
"PASSWORD"
]
},
"serviceCatalog":[
{
"name":"cloudFilesCDN",
"endpoints":[
{
"region":"ORD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"DFW",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"SYD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
}
],
"type":"rax:object-cdn"
},
{
"name":"cloudFiles",
"endpoints":[
{
"region":"ORD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"internalURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"DFW",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"internalURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
},
{
"region":"SYD",
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"publicURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"internalURL":"URL/v1\/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9"
}
],
"type":"object-store"
},
{
"name":"cloudLoadBalancers",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:load-balancer"
},
{
"name":"cloudDatabases",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:database"
},
{
"name":"cloudBlockStorage",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v1\/888888"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1\/888888"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1\/888888"
}
],
"type":"volume"
},
{
"name":"cloudServersOpenStack",
"endpoints":[
{
"region":"SYD",
"tenantId":"888888",
"publicURL":"URL/v2\/888888",
"versionInfo":"https:\/\/syd.servers.api.rackspacecloud.com\/v2",
"versionList":"https:\/\/syd.servers.api.rackspacecloud.com\/",
"versionId":"2"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v2\/888888",
"versionInfo":"https:\/\/dfw.servers.api.rackspacecloud.com\/v2",
"versionList":"https:\/\/dfw.servers.api.rackspacecloud.com\/",
"versionId":"2"
},
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v2\/888888",
"versionInfo":"https:\/\/ord.servers.api.rackspacecloud.com\/v2",
"versionList":"https:\/\/ord.servers.api.rackspacecloud.com\/",
"versionId":"2"
}
],
"type":"compute"
},
{
"name":"autoscale",
"endpoints":[
{
"region":"ORD",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888",
"versionInfo":null,
"versionList":null,
"versionId":"1.0"
},
{
"region":"DFW",
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888",
"versionInfo":null,
"versionList":null,
"versionId":"1.0"
}
],
"type":"rax:autoscale"
},
{
"name":"cloudMonitoring",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:monitor"
},
{
"name":"cloudBackup",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:backup"
},
{
"name":"cloudServers",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888",
"versionInfo":"https:\/\/servers.api.rackspacecloud.com\/v1.0",
"versionList":"https:\/\/servers.api.rackspacecloud.com\/",
"versionId":"1.0"
}
],
"type":"compute"
},
{
"name":"cloudDNS",
"endpoints":[
{
"tenantId":"888888",
"publicURL":"URL/v1.0\/888888"
}
],
"type":"rax:dns"
}
],
"user":{
"id":"335853",
"roles":[
{
"id":"10000150",
"description":"Checkmate Access role",
"name":"checkmate"
},
{
"tenantId":"MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9",
"id":"5",
"description":"A Role that allows a user access to keystone Service methods",
"name":"object-store:default"
},
{
"tenantId":"888888",
"id":"6",
"description":"A Role that allows a user access to keystone Service methods",
"name":"compute:default"
},
{
"id":"3",
"description":"User Admin Role.",
"name":"identity:user-admin"
}
],
"name":"test",
"RAX-AUTH:defaultRegion":"ORD"
}
}
}

View File

@ -0,0 +1,12 @@
[
{
"name": "test_container_1",
"count": 2,
"bytes": 78
},
{
"name": "test_container_2",
"count": 1,
"bytes": 17
}
]

View File

@ -0,0 +1,71 @@
<?xml version="1.0"?>
<!--
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.
-->
<configuration scan="false">
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds-wire.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="BLOBSTOREFILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds-blobstore.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<root>
<level value="warn" />
</root>
<logger name="org.jclouds">
<level value="DEBUG" />
<appender-ref ref="FILE" />
</logger>
<!--
<logger name="jclouds.wire">
<level value="DEBUG" />
<appender-ref ref="WIREFILE" />
</logger>
-->
<logger name="jclouds.headers">
<level value="DEBUG" />
<appender-ref ref="WIREFILE" />
</logger>
<logger name="jclouds.blobstore">
<level value="DEBUG" />
<appender-ref ref="BLOBSTOREFILE" />
</logger>
</configuration>

View File

@ -0,0 +1,23 @@
[
{
"name": "test_obj_1",
"hash": "4281c348eaf83e70ddce0e07221c3d28",
"bytes": 14,
"content_type": "application/octet-stream",
"last_modified": "2009-02-03T05:26:32.612278"
},
{
"name": "test_obj_2",
"hash": "b039efe731ad111bc1b0ef221c3849d0",
"bytes": 64,
"content_type": "application/octet-stream",
"last_modified": "2009-02-03T05:26:32.612278"
},
{
"name": "test obj 3",
"hash": "0b2e80bd0744d9ebb20484149a57c82e",
"bytes": 123,
"content_type": "application/octet-stream",
"last_modified": "2014-05-20T05:26:32.612278"
}
]

View File

@ -45,6 +45,7 @@
<module>openstack-cinder</module>
<module>openstack-nova</module>
<module>openstack-nova-ec2</module>
<module>openstack-swift</module>
<module>openstack-trove</module>
<module>cloudfiles</module>
<module>cloudservers</module>