Issue 75: container create/list support

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1622 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-13 22:46:19 +00:00
parent 8588776eb0
commit c4d8f5ff96
20 changed files with 1124 additions and 12 deletions

View File

@ -0,0 +1,49 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.jclouds.cloud.CloudContext;
import com.google.inject.BindingAnnotation;
/**
* Represents an authenticated context to Cloud Files.
*
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090311.pdf" />
* @see CloudFilesConnection
* @see CloudContext
* @author Adrian Cole
*
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
public @interface Authentication {
}

View File

@ -0,0 +1,49 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.jclouds.cloud.CloudContext;
import com.google.inject.BindingAnnotation;
/**
* Represents an authenticated context to Cloud Files.
*
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090311.pdf" />
* @see CloudFilesConnection
* @see CloudContext
* @author Adrian Cole
*
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
public @interface CDN {
}

View File

@ -0,0 +1,66 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles;
import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import org.jclouds.rackspace.cloudfiles.functions.ParseAuthenticationResponseFromHeaders;
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders;
import org.jclouds.rest.ResponseParser;
/**
* Provides access to Cloud Files via their REST API.
* <p/>
* All commands return a Future of the result from Cloud Files. Any exceptions incurred during
* processing will be wrapped in an {@link ExecutionException} as documented in {@link Future#get()}.
*
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090311.pdf" />
* @author Adrian Cole
*/
public interface CloudFilesAuthentication {
public interface AuthenticationResponse {
@Storage
URI getStorageUrl();
@CDN
URI getCDNManagementUrl();
@Authentication
String getAuthToken();
}
@GET
@ResponseParser(ParseAuthenticationResponseFromHeaders.class)
@Path("/auth")
AuthenticationResponse authenticate(@HeaderParam(CloudFilesHeaders.AUTH_USER) String user,
@HeaderParam(CloudFilesHeaders.AUTH_KEY) String key);
}

View File

@ -23,9 +23,23 @@
*/
package org.jclouds.rackspace.cloudfiles;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
import org.jclouds.rackspace.cloudfiles.functions.ParseContainerListFromGsonResponse;
import org.jclouds.rackspace.filters.AuthenticateRequest;
import org.jclouds.rest.Query;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.ResponseParser;
import org.jclouds.rest.SkipEncoding;
/**
* Provides access to Cloud Files via their REST API.
* <p/>
@ -35,6 +49,18 @@ import java.util.concurrent.Future;
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090311.pdf" />
* @author Adrian Cole
*/
@SkipEncoding('/')
@RequestFilters(AuthenticateRequest.class)
public interface CloudFilesConnection {
@GET
@ResponseParser(ParseContainerListFromGsonResponse.class)
@Query(key = "format", value = "json")
@Path("/")
List<ContainerMetadata> listOwnedContainers();
@PUT
@Path("{container}")
boolean putContainer(@PathParam("container") String container);
}

View File

@ -34,8 +34,8 @@ import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS;
import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTION_REUSE;
import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_MAX_SESSION_FAILURES;
import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_REQUEST_INVOKER_THREADS;
import static org.jclouds.rackspace.reference.RackSpaceConstants.PROPERTY_RACKSPACE_KEY;
import static org.jclouds.rackspace.reference.RackSpaceConstants.PROPERTY_RACKSPACE_USER;
import static org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants.PROPERTY_CLOUDFILES_KEY;
import static org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants.PROPERTY_CLOUDFILES_USER;
import java.util.List;
import java.util.Properties;
@ -43,6 +43,7 @@ import java.util.Properties;
import org.jclouds.cloud.CloudContextBuilder;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
import org.jclouds.rackspace.cloudfiles.config.RestCloudFilesConnectionModule;
import com.google.inject.Injector;
import com.google.inject.Module;
@ -71,8 +72,8 @@ public class CloudFilesContextBuilder extends
Properties properties = new Properties();
properties.setProperty(PROPERTY_HTTP_ADDRESS, "api.mosso.com");
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
properties.setProperty(PROPERTY_HTTP_SECURE, "true");
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, "5");
properties.setProperty(PROPERTY_HTTP_MAX_REDIRECTS, "5");
properties.setProperty(PROPERTY_POOL_MAX_CONNECTION_REUSE, "75");
@ -87,8 +88,8 @@ public class CloudFilesContextBuilder extends
}
public void authenticate(String id, String secret) {
properties.setProperty(PROPERTY_RACKSPACE_USER, checkNotNull(id, "user"));
properties.setProperty(PROPERTY_RACKSPACE_KEY, checkNotNull(secret, "key"));
properties.setProperty(PROPERTY_CLOUDFILES_USER, checkNotNull(id, "user"));
properties.setProperty(PROPERTY_CLOUDFILES_KEY, checkNotNull(secret, "key"));
}
public CloudFilesContext buildContext() {
@ -104,6 +105,7 @@ public class CloudFilesContextBuilder extends
}
protected void addConnectionModule(List<Module> modules) {
modules.add(new RestCloudFilesConnectionModule());
// TODO
}

View File

@ -0,0 +1,49 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.jclouds.cloud.CloudContext;
import com.google.inject.BindingAnnotation;
/**
* Represents an authenticated context to Cloud Files.
*
* @see <a href="http://www.rackspacecloud.com/cf-devguide-20090311.pdf" />
* @see CloudFilesConnection
* @see CloudContext
* @author Adrian Cole
*
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
public @interface Storage {
}

View File

@ -0,0 +1,104 @@
package org.jclouds.rackspace.cloudfiles.config;
import java.net.MalformedURLException;
import java.net.URI;
import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.HttpConstants;
import org.jclouds.http.RequiresHttp;
import org.jclouds.rackspace.cloudfiles.Authentication;
import org.jclouds.rackspace.cloudfiles.CDN;
import org.jclouds.rackspace.cloudfiles.CloudFilesAuthentication;
import org.jclouds.rackspace.cloudfiles.CloudFilesConnection;
import org.jclouds.rackspace.cloudfiles.CloudFilesContext;
import org.jclouds.rackspace.cloudfiles.Storage;
import org.jclouds.rackspace.cloudfiles.CloudFilesAuthentication.AuthenticationResponse;
import org.jclouds.rackspace.cloudfiles.internal.GuiceCloudFilesContext;
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants;
import org.jclouds.rest.RestClientFactory;
import org.jclouds.rest.config.JaxrsModule;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
/**
* Configures the S3 connection, including logging and http transport.
*
* @author Adrian Cole
*/
@ConfiguresCloudConnection
@RequiresHttp
public class RestCloudFilesConnectionModule extends AbstractModule {
@Override
protected void configure() {
install(new JaxrsModule());
bind(CloudFilesContext.class).to(GuiceCloudFilesContext.class);
bindErrorHandlers();
bindRetryHandlers();
}
@Provides
@Singleton
protected AuthenticationResponse provideAuthenticationResponse(
@Authentication URI authenticationUri, RestClientFactory factory,
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_USER) String user,
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_KEY) String key) {
return factory.create(authenticationUri, CloudFilesAuthentication.class).authenticate(user,
key);
}
@Provides
@Authentication
protected String provideAuthenticationToken(@Authentication URI authenticationUri,
RestClientFactory factory,
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_USER) String user,
@Named(CloudFilesConstants.PROPERTY_CLOUDFILES_KEY) String key) {
return factory.create(authenticationUri, CloudFilesAuthentication.class).authenticate(user,
key).getAuthToken();
}
@Provides
@Singleton
@Storage
protected URI provideStorageUrl(AuthenticationResponse response) {
return response.getStorageUrl();
}
@Provides
@Singleton
@CDN
protected URI provideCDNUrl(AuthenticationResponse response) {
return response.getCDNManagementUrl();
}
protected void bindErrorHandlers() {
// TODO
}
protected void bindRetryHandlers() {
// TODO retry on 401 by AuthenticateRequest.update()
}
@Singleton
@Provides
@Authentication
protected URI provideAddress(@Named(HttpConstants.PROPERTY_HTTP_ADDRESS) String address,
@Named(HttpConstants.PROPERTY_HTTP_PORT) int port,
@Named(HttpConstants.PROPERTY_HTTP_SECURE) boolean isSecure)
throws MalformedURLException {
return URI.create(String.format("%1$s://%2$s:%3$s", isSecure ? "https" : "http", address,
port));
}
@Provides
@Singleton
protected CloudFilesConnection provideConnection(@Storage URI authenticationUri,
RestClientFactory factory) {
return factory.create(authenticationUri, CloudFilesConnection.class);
}
}

View File

@ -0,0 +1,109 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles.domain;
/**
*
* @author Adrian Cole
*
*/
public class ContainerMetadata {
public ContainerMetadata(String name, long count, long bytes) {
this.name = name;
this.count = count;
this.bytes = bytes;
}
public ContainerMetadata() {
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ContainerMetadata [bytes=").append(bytes).append(", count=").append(count)
.append(", name=").append(name).append("]");
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (bytes ^ (bytes >>> 32));
result = prime * result + (int) (count ^ (count >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ContainerMetadata other = (ContainerMetadata) obj;
if (bytes != other.bytes)
return false;
if (count != other.count)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
private String name;
private long count;
private long bytes;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setCount(long count) {
this.count = count;
}
public long getCount() {
return count;
}
public void setBytes(long bytes) {
this.bytes = bytes;
}
public long getBytes() {
return bytes;
}
}

View File

@ -0,0 +1,73 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI;
import org.jclouds.http.HttpResponse;
import org.jclouds.rackspace.cloudfiles.CloudFilesAuthentication.AuthenticationResponse;
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders;
import com.google.common.base.Function;
/**
* This parses {@link AuthenticationResponse} from HTTP headers.
*
* @author Adrian Cole
*/
public class ParseAuthenticationResponseFromHeaders implements
Function<HttpResponse, AuthenticationResponse> {
/**
* parses the http response headers to create a new {@link AuthenticationResponse} object.
*/
public AuthenticationResponse apply(final HttpResponse from) {
return new AuthenticationResponse() {
public String getAuthToken() {
return checkNotNull(from.getFirstHeaderOrNull(CloudFilesHeaders.AUTH_TOKEN),
CloudFilesHeaders.AUTH_TOKEN);
}
public URI getCDNManagementUrl() {
String cdnManagementUrl = checkNotNull(from
.getFirstHeaderOrNull(CloudFilesHeaders.CDN_MANAGEMENT_URL),
CloudFilesHeaders.CDN_MANAGEMENT_URL);
return URI.create(cdnManagementUrl);
}
public URI getStorageUrl() {
String storageUrl = checkNotNull(from
.getFirstHeaderOrNull(CloudFilesHeaders.STORAGE_URL),
CloudFilesHeaders.STORAGE_URL);
return URI.create(storageUrl);
}
};
}
}

View File

@ -0,0 +1,61 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles.functions;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.List;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
/**
* This parses {@link ContainerMetadata} from a gson string.
*
* @author Adrian Cole
*/
public class ParseContainerListFromGsonResponse extends ParseJson<List<ContainerMetadata>>
{
@Inject
public ParseContainerListFromGsonResponse(Gson gson) {
super(gson);
}
public List<ContainerMetadata> apply(InputStream stream) {
Type listType = new TypeToken<List<ContainerMetadata>>() {
}.getType();
try {
return gson.fromJson(new InputStreamReader(stream, "UTF-8"), listType);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("jclouds requires UTF-8 encoding", e);
}
}
}

View File

@ -0,0 +1,55 @@
package org.jclouds.rackspace.cloudfiles.internal;
import java.io.IOException;
import javax.annotation.Resource;
import org.jclouds.lifecycle.Closer;
import org.jclouds.logging.Logger;
import org.jclouds.rackspace.cloudfiles.CloudFilesConnection;
import org.jclouds.rackspace.cloudfiles.CloudFilesContext;
import com.google.inject.Inject;
import com.google.inject.Injector;
/**
* Uses a Guice Injector to configure the objects served by CloudFilesContext methods.
*
* @author Adrian Cole
* @see Injector
*/
public class GuiceCloudFilesContext implements CloudFilesContext {
@Resource
private Logger logger = Logger.NULL;
private final Injector injector;
private final Closer closer;
@Inject
private GuiceCloudFilesContext(Injector injector, Closer closer) {
this.injector = injector;
this.closer = closer;
}
/**
* {@inheritDoc}
*/
public CloudFilesConnection getConnection() {
return injector.getInstance(CloudFilesConnection.class);
}
/**
* {@inheritDoc}
*
* @see Closer
*/
public void close() {
try {
closer.close();
} catch (IOException e) {
logger.error(e, "error closing content");
}
}
}

View File

@ -33,5 +33,7 @@ import org.jclouds.rackspace.reference.RackSpaceConstants;
*/
public interface CloudFilesConstants extends ObjectStoreConstants, RackSpaceConstants {
public static final String PROPERTY_CLOUDFILES_USER = "jclouds.cloudfiles.user";
public static final String PROPERTY_CLOUDFILES_KEY = "jclouds.cloudfiles.key";
}

View File

@ -39,7 +39,7 @@ public interface CloudFilesHeaders {
public static final String AUTH_TOKEN = "X-Auth-Token";
public static final String AUTH_USER = "X-Auth-User";
public static final String CDN_ENABLED = "X-CDN-Enabled";
public static final String CDN_MANAGEMENT_URL = "X-CDN-Management-URL";
public static final String CDN_MANAGEMENT_URL = "X-CDN-Management-Url";
public static final String CDN_REFERRER_ACL = "X-Referrer-ACL ";
public static final String CDN_TTL = "X-TTL";
public static final String CDN_URI = "X-CDN-URI";

View File

@ -0,0 +1,96 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.filters;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.rackspace.cloudfiles.Authentication;
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
/**
* Signs the Cloud Files request. This will update the Authentication Token before 24 hours is up.
*
* @author Adrian Cole
*
*/
@Singleton
public class AuthenticateRequest implements HttpRequestFilter {
private final Provider<String> authTokenProvider;
public final long BILLION = 1000000000;
public final long MINUTES = 60 * BILLION;
public final long HOURS = 60 * MINUTES;
private final AtomicReference<String> authToken;
private final AtomicLong trigger = new AtomicLong(0);
/**
* Start the time update service. Cloud Files clocks need to be 24 hours of the auth token. This
* is not performed per-request, as creation of the token is a slow, synchronized command.
*/
synchronized void updateIfTimeOut() {
if (trigger.get() - System.nanoTime() <= 0) {
createNewToken();
}
}
// this is a hotspot when submitted concurrently, so be lazy.
// cloudfiles is ok with up to 23:59 off their time, so let's
// be as lazy as possible.
public String createNewToken() {
authToken.set(authTokenProvider.get());
trigger.set(System.nanoTime() + System.nanoTime() + 23 * HOURS);
return authToken.get();
}
public String getAuthToken() {
updateIfTimeOut();
return authToken.get();
}
@Inject
public AuthenticateRequest(@Authentication Provider<String> authTokenProvider) {
this.authTokenProvider = authTokenProvider;
authToken = new AtomicReference<String>();
}
public void filter(HttpRequest request) throws HttpException {
request.getHeaders().replaceValues(CloudFilesHeaders.AUTH_TOKEN,
Collections.singletonList(getAuthToken()));
}
}

View File

@ -0,0 +1,107 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles;
import static org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants.PROPERTY_CLOUDFILES_KEY;
import static org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants.PROPERTY_CLOUDFILES_USER;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import org.jclouds.concurrent.WithinThreadExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.http.HttpResponseException;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.rackspace.cloudfiles.CloudFilesAuthentication.AuthenticationResponse;
import org.jclouds.rest.RestClientFactory;
import org.jclouds.rest.config.JaxrsModule;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Singleton;
/**
* Tests behavior of {@code JaxrsAnnotationProcessor}
*
* @author Adrian Cole
*/
@Test(groups = "live", testName = "cloudfiles.CloudFilesAuthenticationLiveTest")
public class CloudFilesAuthenticationLiveTest {
protected static final String sysRackspaceUser = System.getProperty(PROPERTY_CLOUDFILES_USER);
protected static final String sysRackspaceKey = System.getProperty(PROPERTY_CLOUDFILES_KEY);
private Injector injector;
@Test
public void testAuthentication() throws Exception {
CloudFilesAuthentication authentication = injector
.getInstance(CloudFilesAuthentication.class);
AuthenticationResponse response = authentication.authenticate(sysRackspaceUser,
sysRackspaceKey);
assertNotNull(response);
assertNotNull(response.getStorageUrl());
assertNotNull(response.getCDNManagementUrl());
assertNotNull(response.getAuthToken());
}
@Test(expectedExceptions = HttpResponseException.class)
public void testBadAuthentication() throws Exception {
CloudFilesAuthentication authentication = injector
.getInstance(CloudFilesAuthentication.class);
try {
authentication.authenticate("foo", "bar");
} catch (UndeclaredThrowableException e) {
HttpResponseException ew = (HttpResponseException) e.getCause().getCause();
assertEquals(ew.getResponse().getStatusCode(), 401);
throw ew;
}
fail();
}
@BeforeClass
void setupFactory() {
injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
}
@SuppressWarnings("unused")
@Provides
@Singleton
protected CloudFilesAuthentication provideCloudFilesAuthentication(
RestClientFactory factory) {
return factory.create(URI.create("https://api.mosso.com"),
CloudFilesAuthentication.class);
}
}, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()),
new JavaUrlHttpCommandExecutorServiceModule());
}
}

View File

@ -0,0 +1,89 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles;
import static org.testng.Assert.assertEquals;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import org.jclouds.concurrent.WithinThreadExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.http.HttpMethod;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.rackspace.cloudfiles.functions.ParseAuthenticationResponseFromHeaders;
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders;
import org.jclouds.rest.JaxrsAnnotationProcessor;
import org.jclouds.rest.config.JaxrsModule;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
/**
* Tests behavior of {@code JaxrsAnnotationProcessor}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "cloudfiles.CloudFilesAuthentication")
public class CloudFilesAuthenticationTest {
JaxrsAnnotationProcessor.Factory factory;
public void testAuthenticate() throws SecurityException, NoSuchMethodException {
Method method = CloudFilesAuthentication.class.getMethod("authenticate", String.class,
String.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(CloudFilesAuthentication.class).createRequest(
endpoint, method, new Object[] { "foo", "bar" });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/auth");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 2);
assertEquals(httpMethod.getHeaders().get(CloudFilesHeaders.AUTH_USER), Collections
.singletonList("foo"));
assertEquals(httpMethod.getHeaders().get(CloudFilesHeaders.AUTH_KEY), Collections
.singletonList("bar"));
factory.create(CloudFilesAuthentication.class);
assertEquals(JaxrsAnnotationProcessor.getParserOrThrowException(method),
ParseAuthenticationResponseFromHeaders.class);
}
@BeforeClass
void setupFactory() {
factory = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(URI.class).toInstance(URI.create("http://localhost:8080"));
}
}, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()),
new JavaUrlHttpCommandExecutorServiceModule()).getInstance(
JaxrsAnnotationProcessor.Factory.class);
}
}

View File

@ -0,0 +1,69 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles;
import static org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants.PROPERTY_CLOUDFILES_KEY;
import static org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants.PROPERTY_CLOUDFILES_USER;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.util.List;
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
import org.testng.annotations.Test;
/**
* Tests behavior of {@code JaxrsAnnotationProcessor}
*
* @author Adrian Cole
*/
@Test(groups = "live", testName = "cloudfiles.CloudFilesAuthenticationLiveTest")
public class CloudFilesConnectionLiveTest {
protected static final String sysRackspaceUser = System.getProperty(PROPERTY_CLOUDFILES_USER);
protected static final String sysRackspaceKey = System.getProperty(PROPERTY_CLOUDFILES_KEY);
private String bucketPrefix = System.getProperty("user.name") + ".cfint";
@Test
public void testListOwnedContainers() throws Exception {
CloudFilesConnection connection = CloudFilesContextBuilder.newBuilder(sysRackspaceUser,
sysRackspaceKey).withJsonDebug().buildContext().getConnection();
List<ContainerMetadata> response = connection.listOwnedContainers();
assertNotNull(response);
}
@Test
public void testPutContainers() throws Exception {
CloudFilesConnection connection = CloudFilesContextBuilder.newBuilder(sysRackspaceUser,
sysRackspaceKey).withJsonDebug().buildContext().getConnection();
assertTrue(connection.putContainer(bucketPrefix + ".hello"));
List<ContainerMetadata> response = connection.listOwnedContainers();
assertNotNull(response);
assertEquals(response.size(), 1);
assertEquals(response.get(0).getName(), bucketPrefix + ".hello");
}
}

View File

@ -0,0 +1,56 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.rackspace.cloudfiles.functions;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
/**
* Tests behavior of {@code ParseContainerListFromGsonResponse}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "cloudfiles.ParseContainerListFromGsonResponse")
public class ParseContainerListFromGsonResponseTest {
@Test
public void testApplyInputStream() {
InputStream is = IOUtils
.toInputStream("[ {\"name\":\"test_container_1\",\"count\":2,\"bytes\":78}, {\"name\":\"test_container_2\",\"count\":1,\"bytes\":17} ] ");
List<ContainerMetadata> expects = ImmutableList.of(new ContainerMetadata("test_container_1",
2, 78), new ContainerMetadata("test_container_2", 1, 17));
ParseContainerListFromGsonResponse parser = new ParseContainerListFromGsonResponse(new Gson());
assertEquals(parser.apply(is), expects);
}
}

View File

@ -39,6 +39,10 @@
<modules>
<module>core</module>
</modules>
<properties>
<jclouds.cloudfiles.user />
<jclouds.cloudfiles.key />
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
@ -65,4 +69,53 @@
<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>
<!-- note that the groups/excluded groups don't work due to some problem
in surefire or testng. instead, we have to exclude via file path
<groups>live,integration</groups>
<excludedGroups>unit,performance</excludedGroups> -->
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/*IntegrationTest.java</include>
<include>**/*LiveTest.java</include>
</includes>
<systemProperties>
<property>
<name>file.encoding</name>
<value>UTF-8</value>
</property>
<property>
<name>jclouds.cloudfiles.user</name>
<value>${jclouds.cloudfiles.user}</value>
</property>
<property>
<name>jclouds.cloudfiles.key</name>
<value>${jclouds.cloudfiles.key}</value>
</property>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -30,7 +30,4 @@ package org.jclouds.rackspace.reference;
*/
public interface RackSpaceConstants {
public static final String PROPERTY_RACKSPACE_USER = "jclouds.rackspace.user";
public static final String PROPERTY_RACKSPACE_KEY = "jclouds.rackspace.key";
}