Merge branch 'master' of git://github.com/jclouds/jclouds

This commit is contained in:
andreaturli 2010-11-06 12:02:03 +01:00
commit 25dbce571d
18 changed files with 155 additions and 53 deletions

View File

@ -19,22 +19,26 @@
Overview:
jclouds is an open source framework that helps you get started in the cloud
jclouds is an open source library that helps you get started in the cloud
and reuse your java and clojure development skills. Our api allows you to
freedom to use portable abstractions or cloud-specific features.
freedom to use portable abstractions or cloud-specific features. We have
two abstractions at the moment: compute and blobstore. compute helps you
bootstrap machines in the cloud. blobstore helps you manage key-value
data.
our current version is 1.0-beta-7
our dev version is 1.0-SNAPSHOT
our compute api supports: ec2, gogrid, rackspace, rimuhosting, vcloud, trmk-ecloud,
trmk-vcloudexpress, eucalyptus, bluelock-vclouddirector,
bluelock-vcloudexpress, slicehost, stub (in-memory)
our compute api supports: ec2, gogrid, cloudservers (rackspace), rimuhosting, vcloud,
trmk-ecloud, trmk-vcloudexpress, eucalyptus,
bluelock-vclouddirector, slicehost, stub (in-memory)
* note * the pom dependency org.jclouds/jclouds-allcompute gives you access to
to all of these providers
our blobstore api supports: s3, rackspace, azure, atmos online, att synaptic,
walrus, googlestorage, transient (in-memory), filesystem (on-disk)
our blobstore api supports: s3, cloudfiles (rackspace), azurestorage, atmosonline,
synaptic, peer1-storage, walrus, googlestorage,
transient (in-memory), filesystem (on-disk)
* note * the pom dependency org.jclouds/jclouds-allblobstore gives you access to
to all of these providers

View File

@ -36,6 +36,10 @@
<test.atmosonline.apiversion>1.3.0</test.atmosonline.apiversion>
<test.atmosonline.identity>FIXME</test.atmosonline.identity>
<test.atmosonline.credential>FIXME</test.atmosonline.credential>
<test.peer1-storage.endpoint>https://cloudonestorage.peer1.com</test.peer1-storage.endpoint>
<test.peer1-storage.apiversion>1.3.0</test.peer1-storage.apiversion>
<test.peer1-storage.identity>FIXME</test.peer1-storage.identity>
<test.peer1-storage.credential>FIXME</test.peer1-storage.credential>
<test.synaptic.endpoint>https://storage.synaptic.att.com</test.synaptic.endpoint>
<test.synaptic.apiversion>1.3.0</test.synaptic.apiversion>
<test.synaptic.identity>FIXME</test.synaptic.identity>
@ -107,6 +111,22 @@
<name>test.atmosonline.credential</name>
<value>${test.atmosonline.credential}</value>
</property>
<property>
<name>test.peer1-storage.endpoint</name>
<value>${test.peer1-storage.endpoint}</value>
</property>
<property>
<name>test.peer1-storage.apiversion</name>
<value>${test.peer1-storage.apiversion}</value>
</property>
<property>
<name>test.peer1-storage.identity</name>
<value>${test.peer1-storage.identity}</value>
</property>
<property>
<name>test.peer1-storage.credential</name>
<value>${test.peer1-storage.credential}</value>
</property>
<property>
<name>test.synaptic.endpoint</name>
<value>${test.synaptic.endpoint}</value>

View File

@ -29,6 +29,7 @@ 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 javax.ws.rs.core.MediaType;
import org.jclouds.atmosonline.saas.binders.BindMetadataToHeaders;
@ -103,6 +104,7 @@ public interface AtmosStorageAsyncClient {
@POST
@Path("/{directoryName}/")
@ExceptionParser(ReturnEndpointIfAlreadyExists.class)
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.WILDCARD)
ListenableFuture<URI> createDirectory(@PathParam("directoryName") String directoryName);

View File

@ -148,7 +148,7 @@ public class SignRequest implements HttpRequestFilter {
// TreeSet == Sort the headers alphabetically.
Set<String> headers = new TreeSet<String>(request.getHeaders().keySet());
for (String header : headers) {
if (header.startsWith("x-emc-")) {
if (header.startsWith("x-emc-") && !header.equals(AtmosStorageHeaders.SIGNATURE)) {
// Convert all header names to lowercase.
toSign.append(header.toLowerCase()).append(":");
// For headers with values that span multiple lines, convert them into one line by

View File

@ -140,7 +140,7 @@ public class AtmosStorageAsyncClientTest extends RestClientTest<AtmosStorageAsyn
assertRequestLineEquals(request, "POST https://accesspoint.atmosonline.com/rest/namespace/dir/ HTTP/1.1");
assertNonPayloadHeadersEqual(request, HttpHeaders.ACCEPT + ": */*\n");
assertPayloadEquals(request, null, null, false);
assertPayloadEquals(request, "", "application/octet-stream", false);
assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class);
assertSaxResponseParserClassEquals(method, null);

View File

@ -89,7 +89,6 @@ public class ParseAzureStorageErrorFromXmlContent implements HttpErrorHandler {
exception = new AuthorizationException(command.getRequest(), message);
break;
case 404:
if (!command.getRequest().getMethod().equals("DELETE")) {
String path = command.getRequest().getEndpoint().getPath();
Matcher matcher = CONTAINER_PATH.matcher(path);

View File

@ -28,6 +28,7 @@ import static org.easymock.classextension.EasyMock.verify;
import java.net.URI;
import org.easymock.IArgumentMatcher;
import org.jclouds.azure.storage.AzureStorageResponseException;
import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpRequest;
@ -55,6 +56,14 @@ public class ParseAzureErrorFromXmlContentTest {
IllegalArgumentException.class);
}
@Test
public void test412WithTextHtmlHttpResponseException() {
assertCodeMakes("GET", URI
.create("https://jclouds.blob.core.windows.net/adriancole-blobstore2?restype=container&comp=list&prefix=apps/apps/apps/&include=metadata"), 412,
"HTTP/1.1 412 The condition specified using HTTP conditional header(s) is not met.", "application/xml", "<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>ConditionNotMet</Code><Message>The condition specified using HTTP conditional header(s) is not met.\nRequestId:921efcad-84bc-4e0a-863d-24810d1096e1\nTime:2010-11-04T15:03:07.8694513Z</Message></Error>",
AzureStorageResponseException.class);
}
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
String content, Class<? extends Exception> expected) {

View File

@ -21,7 +21,8 @@
"A clojure binding for the jclouds BlobStore.
Current supported services are:
[transient, filesystem, s3, azureblob, atmos, cloudfiles, walrus, googlestorage]
[transient, filesystem, s3, azureblob, atmos, cloudfiles, walrus,
googlestorage, synaptic, peer1-storage]
Here's a quick example of how to viewresources in rackspace

View File

@ -35,6 +35,7 @@ import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.concurrent.Timeout;
import org.jclouds.http.HttpResponseException;
import org.jclouds.internal.ClassMethodArgs;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
@ -57,7 +58,7 @@ public class SyncProxy implements InvocationHandler {
@SuppressWarnings("unchecked")
public static <T> T proxy(Class<T> clazz, SyncProxy proxy) throws IllegalArgumentException, SecurityException,
NoSuchMethodException {
NoSuchMethodException {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, proxy);
}
@ -72,8 +73,8 @@ public class SyncProxy implements InvocationHandler {
@Inject
public SyncProxy(Class<?> declaring, Object async,
@Named("sync") ConcurrentMap<ClassMethodArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async)
throws SecurityException, NoSuchMethodException {
@Named("sync") ConcurrentMap<ClassMethodArgs, Object> delegateMap, Map<Class<?>, Class<?>> sync2Async)
throws SecurityException, NoSuchMethodException {
this.delegateMap = delegateMap;
this.delegate = async;
this.declaring = declaring;
@ -92,7 +93,7 @@ public class SyncProxy implements InvocationHandler {
Method delegatedMethod = delegate.getClass().getMethod(method.getName(), method.getParameterTypes());
if (!Arrays.equals(delegatedMethod.getExceptionTypes(), method.getExceptionTypes()))
throw new IllegalArgumentException(String.format(
"method %s has different typed exceptions than delegated method %s", method, delegatedMethod));
"method %s has different typed exceptions than delegated method %s", method, delegatedMethod));
if (delegatedMethod.getReturnType().isAssignableFrom(ListenableFuture.class)) {
if (method.isAnnotationPresent(Timeout.class)) {
Timeout methodTimeout = method.getAnnotation(Timeout.class);
@ -124,7 +125,7 @@ public class SyncProxy implements InvocationHandler {
} else if (method.isAnnotationPresent(Delegate.class)) {
Class<?> asyncClass = sync2Async.get(method.getReturnType());
checkState(asyncClass != null, "please configure corresponding async class for " + method.getReturnType()
+ " in your RestClientModule");
+ " in your RestClientModule");
Object returnVal = delegateMap.get(new ClassMethodArgs(asyncClass, method, args));
return returnVal;
} else if (syncMethodMap.containsKey(method)) {
@ -132,7 +133,7 @@ public class SyncProxy implements InvocationHandler {
} else {
try {
return ((ListenableFuture<?>) methodMap.get(method).invoke(delegate, args)).get(timeoutMap.get(method),
TimeUnit.NANOSECONDS);
TimeUnit.NANOSECONDS);
} catch (ProvisionException e) {
throw throwTypedExceptionOrCause(method.getExceptionTypes(), e);
} catch (ExecutionException e) {
@ -143,6 +144,7 @@ public class SyncProxy implements InvocationHandler {
}
}
// Note this needs to be kept up-to-date with all top-level exceptions jclouds works against
@SuppressWarnings("unchecked")
public static Exception throwTypedExceptionOrCause(Class[] exceptionTypes, Exception exception) throws Exception {
for (Class type : exceptionTypes) {
@ -154,6 +156,7 @@ public class SyncProxy implements InvocationHandler {
Throwables.propagateIfInstanceOf(exception, IllegalStateException.class);
Throwables.propagateIfInstanceOf(exception, AuthorizationException.class);
Throwables.propagateIfInstanceOf(exception, ResourceNotFoundException.class);
Throwables.propagateIfInstanceOf(exception, HttpResponseException.class);
Throwables.throwCause(exception, true);
return exception;
}

View File

@ -26,12 +26,10 @@ import static com.google.common.io.Closeables.closeQuietly;
import java.io.InputStream;
import java.io.StringReader;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.logging.Logger;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Utils;
@ -41,6 +39,7 @@ import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
/**
* This object will parse the body of an HttpResponse and return the result of type <T> back to the
@ -52,8 +51,6 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
private final XMLReader parser;
private final HandlerWithResult<T> handler;
@Resource
protected Logger logger = Logger.NULL;
private HttpRequest request;
public static interface Factory {
@ -71,7 +68,7 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
checkNotNull(from, "http response");
checkNotNull(from.getPayload(), "payload in " + from);
} catch (NullPointerException e) {
return addRequestDetailsToException(e);
return addDetailsAndPropagate(from, e);
}
if (from.getStatusCode() >= 300)
return convertStreamToStringAndParse(from);
@ -82,7 +79,7 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
try {
return parse(Utils.toStringAndClose(from.getPayload().getInput()));
} catch (Exception e) {
return addRequestDetailsToException(e);
return addDetailsAndPropagate(from, e);
}
}
@ -91,7 +88,7 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
checkNotNull(from, "xml string");
checkArgument(from.indexOf('<') >= 0, String.format("not an xml document [%s] ", from));
} catch (RuntimeException e) {
return addRequestDetailsToException(e);
return addDetailsAndPropagate(null, e);
}
return parse(new InputSource(new StringReader(from)));
}
@ -113,31 +110,39 @@ public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext
parser.parse(from);
return getHandler().getResult();
} catch (Exception e) {
return addRequestDetailsToException(e);
return addDetailsAndPropagate(null, e);
}
}
private T addRequestDetailsToException(Exception e) {
String exceptionMessage = e.getMessage();
public T addDetailsAndPropagate(HttpResponse response, Exception e) {
StringBuilder message = new StringBuilder();
if (request != null) {
message.append("request: ").append(request.getRequestLine());
}
if (response != null) {
if (message.length() != 0)
message.append("; ");
message.append("response: ").append(response.getStatusLine());
}
if (e instanceof SAXParseException) {
SAXParseException parseException = (SAXParseException) e;
String systemId = parseException.getSystemId();
if (systemId == null) {
systemId = "";
}
exceptionMessage = String.format("Error on line %d of document %s: %s", systemId, parseException
.getLineNumber(), parseException.getMessage());
if (message.length() != 0)
message.append("; ");
message.append(String.format("error at %d:%d in document %s", parseException.getColumnNumber(),
parseException.getLineNumber(), systemId));
}
if (request != null) {
StringBuilder message = new StringBuilder();
message.append("Error parsing input for ").append(request.getRequestLine()).append(": ");
message.append(exceptionMessage);
logger.error(e, message.toString());
if (message.length() != 0) {
message.append("; cause: ").append(e.toString());
throw new RuntimeException(message.toString(), e);
} else {
logger.error(e, exceptionMessage.toString());
throw new RuntimeException(exceptionMessage.toString(), e);
Throwables.propagate(e);
return null;
}
}
public HandlerWithResult<T> getHandler() {

View File

@ -33,7 +33,7 @@ import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.collect.Sets.newTreeSet;
import static java.util.Arrays.asList;
import static javax.ws.rs.core.HttpHeaders.ACCEPT;
import static javax.ws.rs.core.HttpHeaders.*;
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
import static javax.ws.rs.core.HttpHeaders.HOST;
import static org.jclouds.http.HttpUtils.makeQueryLine;
@ -128,6 +128,7 @@ import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.MapMaker;
@ -470,6 +471,9 @@ public class RestAnnotationProcessor<T> {
payload = new MultipartForm(BOUNDARY, parts);
} else if (formParams.size() > 0) {
payload = Payloads.newUrlEncodedFormPayload(formParams, skips);
} else if (headers.containsKey(CONTENT_TYPE)) {
payload = Payloads.newByteArrayPayload(new byte[]{});
payload.getContentMetadata().setContentType(Iterables.get(headers.get(CONTENT_TYPE),0));
}
if (payload != null) {
request.setPayload(payload);

View File

@ -104,7 +104,11 @@ synaptic.contextbuilder=org.jclouds.atmosonline.saas.AtmosStorageContextBuilder
synaptic.endpoint=https://storage.synaptic.att.com
synaptic.apiversion=1.3.0
# TODO peer1 and hostedsolutions use atmos
peer1-storage.contextbuilder=org.jclouds.atmosonline.saas.AtmosStorageContextBuilder
peer1-storage.endpoint=https://storage.synaptic.att.com
peer1-storage.apiversion=1.3.0
# TODO hostedsolutions use atmos
cloudfiles.contextbuilder=org.jclouds.rackspace.cloudfiles.CloudFilesContextBuilder
cloudfiles.propertiesbuilder=org.jclouds.rackspace.cloudfiles.CloudFilesPropertiesBuilder

View File

@ -374,9 +374,21 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@Path("")
public void post(HttpRequestOptions options);
@POST
@Path("")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public void post();
}
public void testHttpRequestOptionsPayloadParam() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("post");
HttpRequest request = factory(TestQuery.class).createRequest(method);
assertRequestLineEquals(request, "POST http://localhost:9999?x-ms-version=2009-07-17 HTTP/1.1");
assertNonPayloadHeadersEqual(request, "");
assertPayloadEquals(request, "", "application/octet-stream", false);
}
public void testHttpRequestWithOnlyContentType() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPayloadParamVarargs.class.getMethod("post", HttpRequestOptions.class);
verifyTestPostOptions(method);
}

View File

@ -33,9 +33,10 @@
<properties>
<!-- when instances are hung, open a ticket and add here -->
<jclouds.compute.blacklist.nodes>trmkrun-ccc,test.trmk-924</jclouds.compute.blacklist.nodes>
<test.servermanager-compute.endpoint>https://servermanager-compute.com</test.servermanager-compute.endpoint>
<test.servermanager-compute.apiversion>1.0</test.servermanager-compute.apiversion>
<test.servermanager-compute.identity>FIXME</test.servermanager-compute.identity>
<test.servermanager.identity>FIXME</test.servermanager.identity>
<test.servermanager.endpoint>https://servermanager.com</test.servermanager.endpoint>
<test.servermanager.apiversion>1.0</test.servermanager.apiversion>
<test.servermanager.identity>FIXME</test.servermanager.identity>
</properties>
<dependencies>
<dependency>
@ -95,16 +96,16 @@
<configuration>
<systemProperties>
<property>
<name>test.servermanager-compute.endpoint</name>
<value>${test.servermanager-compute.endpoint}</value>
<name>test.servermanager.endpoint</name>
<value>${test.servermanager.endpoint}</value>
</property>
<property>
<name>test.servermanager-compute.apiversion</name>
<value>${test.servermanager-compute.apiversion}</value>
<name>test.servermanager.apiversion</name>
<value>${test.servermanager.apiversion}</value>
</property>
<property>
<name>test.servermanager-compute.identity</name>
<value>${test.servermanager-compute.identity}</value>
<name>test.servermanager.identity</name>
<value>${test.servermanager.identity}</value>
</property>
<property>
<name>jclouds.compute.blacklist.nodes</name>

View File

@ -25,7 +25,7 @@ import java.util.Properties;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
import org.jclouds.vcloud.VCloudExpressContextBuilder;
import org.jclouds.vcloud.terremark.compute.config.TerremarkVCloudComputeServiceContextModule;
import org.jclouds.vcloud.terremark.compute.config.TerremarkECloudComputeServiceContextModule;
import org.jclouds.vcloud.terremark.config.TerremarkECloudRestClientModule;
import com.google.inject.Injector;
@ -52,7 +52,7 @@ public class TerremarkECloudContextBuilder extends VCloudExpressContextBuilder {
@Override
protected void addContextModule(List<Module> modules) {
modules.add(new TerremarkVCloudComputeServiceContextModule());
modules.add(new TerremarkECloudComputeServiceContextModule());
}
@Override

View File

@ -21,6 +21,7 @@ package org.jclouds.vcloud.terremark;
import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
import static org.jclouds.vcloud.terremark.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_NAME;
import static org.jclouds.vcloud.terremark.reference.TerremarkConstants.PROPERTY_TERREMARK_EXTENSION_VERSION;
@ -39,6 +40,8 @@ public class TerremarkECloudPropertiesBuilder extends TerremarkVCloudPropertiesB
properties.setProperty(PROPERTY_ENDPOINT, "https://services.enterprisecloud.terremark.com/api");
properties.setProperty(PROPERTY_TERREMARK_EXTENSION_NAME, "eCloudExtensions");
properties.setProperty(PROPERTY_TERREMARK_EXTENSION_VERSION, "2.5");
// for some reason the centos template is very slow to deploy
properties.setProperty(PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED, 720l * 1000l + "");
return properties;
}

View File

@ -0,0 +1,35 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.vcloud.terremark.compute.config;
import static org.jclouds.compute.domain.OsFamily.CENTOS;
import org.jclouds.compute.domain.TemplateBuilder;
import com.google.inject.Injector;
/**
* @author Adrian Cole
*/
public class TerremarkECloudComputeServiceContextModule extends TerremarkVCloudComputeServiceContextModule {
protected TemplateBuilder provideTemplate(Injector injector, TemplateBuilder template) {
return template.osFamily(CENTOS);
}
}

View File

@ -44,9 +44,9 @@ import org.testng.annotations.Test;
*
* @author Adrian Cole
*/
@Test(groups = "live", enabled = false, sequential = true, testName = "terremark.TerremarkVCloudComputeServiceLiveTest")
public class TerremarkECloudComputeServiceLiveTestDisabled extends BaseComputeServiceLiveTest {
public TerremarkECloudComputeServiceLiveTestDisabled() {
@Test(groups = "live", enabled = true, sequential = true, testName = "terremark.TerremarkVCloudComputeServiceLiveTest")
public class TerremarkECloudComputeServiceLiveTest extends BaseComputeServiceLiveTest {
public TerremarkECloudComputeServiceLiveTest() {
provider = "trmk-ecloud";
}