mirror of https://github.com/apache/jclouds.git
internal enhancements for wrapping json, and specifying an endpoint per delegate
This commit is contained in:
parent
4d2520f910
commit
19672407e7
|
@ -22,9 +22,11 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.json.Json;
|
||||
import org.jclouds.cloudservers.domain.BackupSchedule;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
|
@ -37,17 +39,19 @@ import com.google.common.collect.ImmutableMap;
|
|||
*/
|
||||
@Singleton
|
||||
public class BindBackupScheduleToJsonPayload extends BindToJsonPayload {
|
||||
@Inject
|
||||
public BindBackupScheduleToJsonPayload(Json jsonBinder) {
|
||||
super(jsonBinder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
|
||||
throw new IllegalStateException(
|
||||
"Replace Backup Schedule needs an BackupSchedule object, not a Map");
|
||||
throw new IllegalStateException("Replace Backup Schedule needs an BackupSchedule object, not a Map");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object toBind) {
|
||||
checkArgument(toBind instanceof BackupSchedule,
|
||||
"this binder is only valid for BackupSchedules!");
|
||||
checkArgument(toBind instanceof BackupSchedule, "this binder is only valid for BackupSchedules!");
|
||||
return super.bindToRequest(request, ImmutableMap.of("backupSchedule", toBind));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.cloudservers.domain.Addresses;
|
||||
import org.jclouds.encryption.internal.Base64;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.cloudservers.domain.Addresses;
|
||||
import org.jclouds.rest.MapBinder;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -40,7 +43,9 @@ import com.google.common.collect.Maps;
|
|||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class CreateServerOptions extends BindToJsonPayload {
|
||||
public class CreateServerOptions implements MapBinder {
|
||||
@Inject
|
||||
private BindToJsonPayload jsonBinder;
|
||||
|
||||
static class File {
|
||||
private final String path;
|
||||
|
@ -50,11 +55,9 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
this.path = checkNotNull(path, "path");
|
||||
this.contents = Base64.encodeBytes(checkNotNull(contents, "contents"));
|
||||
checkArgument(path.getBytes().length < 255, String.format(
|
||||
"maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path
|
||||
.getBytes().length));
|
||||
"maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path.getBytes().length));
|
||||
checkArgument(contents.length < 10 * 1024, String.format(
|
||||
"maximum size of the file is 10KB. Contents specified is %d bytes",
|
||||
contents.length));
|
||||
"maximum size of the file is 10KB. Contents specified is %d bytes", contents.length));
|
||||
}
|
||||
|
||||
public String getContents() {
|
||||
|
@ -92,10 +95,9 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
|
||||
ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"),
|
||||
"name parameter not present"), Integer.parseInt(checkNotNull(postParams
|
||||
.get("imageId"), "imageId parameter not present")), Integer.parseInt(checkNotNull(
|
||||
postParams.get("flavorId"), "flavorId parameter not present")));
|
||||
ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present"),
|
||||
Integer.parseInt(checkNotNull(postParams.get("imageId"), "imageId parameter not present")), Integer
|
||||
.parseInt(checkNotNull(postParams.get("flavorId"), "flavorId parameter not present")));
|
||||
if (metadata.size() > 0)
|
||||
server.metadata = metadata;
|
||||
if (files.size() > 0)
|
||||
|
@ -162,19 +164,15 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
*/
|
||||
public CreateServerOptions withMetadata(Map<String, String> metadata) {
|
||||
checkNotNull(metadata, "metadata");
|
||||
checkArgument(metadata.size() <= 5,
|
||||
"you cannot have more then 5 metadata values. You specified: " + metadata.size());
|
||||
checkArgument(metadata.size() <= 5, "you cannot have more then 5 metadata values. You specified: "
|
||||
+ metadata.size());
|
||||
for (Entry<String, String> entry : metadata.entrySet()) {
|
||||
checkArgument(entry.getKey().getBytes().length < 255, String.format(
|
||||
"maximum length of metadata key is 255 bytes. Key specified %s is %d bytes",
|
||||
entry.getKey(), entry.getKey().getBytes().length));
|
||||
checkArgument(
|
||||
entry.getKey().getBytes().length < 255,
|
||||
String
|
||||
.format(
|
||||
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes",
|
||||
entry.getKey(), entry.getValue(),
|
||||
entry.getValue().getBytes().length));
|
||||
"maximum length of metadata key is 255 bytes. Key specified %s is %d bytes", entry.getKey(), entry
|
||||
.getKey().getBytes().length));
|
||||
checkArgument(entry.getKey().getBytes().length < 255, String.format(
|
||||
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes", entry
|
||||
.getKey(), entry.getValue(), entry.getValue().getBytes().length));
|
||||
}
|
||||
this.metadata = metadata;
|
||||
return this;
|
||||
|
@ -196,8 +194,7 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
* sharedIpGroupId is also supplied.
|
||||
*/
|
||||
public CreateServerOptions withSharedIp(String publicIp) {
|
||||
checkState(sharedIpGroupId != null,
|
||||
"sharedIp is invalid unless a shared ip group is specified.");
|
||||
checkState(sharedIpGroupId != null, "sharedIp is invalid unless a shared ip group is specified.");
|
||||
this.publicIp = checkNotNull(publicIp, "ip");
|
||||
return this;
|
||||
}
|
||||
|
@ -237,4 +234,9 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
|
||||
return jsonBinder.bindToRequest(request, input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.rest.MapBinder;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -36,7 +38,10 @@ import com.google.common.collect.ImmutableMap;
|
|||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class CreateSharedIpGroupOptions extends BindToJsonPayload {
|
||||
public class CreateSharedIpGroupOptions implements MapBinder {
|
||||
@Inject
|
||||
private BindToJsonPayload jsonBinder;
|
||||
|
||||
Integer serverId;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -53,9 +58,8 @@ public class CreateSharedIpGroupOptions extends BindToJsonPayload {
|
|||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
|
||||
SharedIpGroupRequest createRequest = new SharedIpGroupRequest(checkNotNull(postParams
|
||||
.get("name")), serverId);
|
||||
return super.bindToRequest(request, ImmutableMap.of("sharedIpGroup", createRequest));
|
||||
SharedIpGroupRequest createRequest = new SharedIpGroupRequest(checkNotNull(postParams.get("name")), serverId);
|
||||
return jsonBinder.bindToRequest(request, ImmutableMap.of("sharedIpGroup", createRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -84,4 +88,5 @@ public class CreateSharedIpGroupOptions extends BindToJsonPayload {
|
|||
return options.withServer(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,10 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.rest.MapBinder;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -34,7 +37,9 @@ import com.google.common.collect.Maps;
|
|||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class RebuildServerOptions extends BindToJsonPayload {
|
||||
public class RebuildServerOptions implements MapBinder {
|
||||
@Inject
|
||||
private BindToJsonPayload jsonBinder;
|
||||
Integer imageId;
|
||||
|
||||
@Override
|
||||
|
@ -42,7 +47,7 @@ public class RebuildServerOptions extends BindToJsonPayload {
|
|||
Map<String, Integer> image = Maps.newHashMap();
|
||||
if (imageId != null)
|
||||
image.put("imageId", imageId);
|
||||
return super.bindToRequest(request, ImmutableMap.of("rebuild", image));
|
||||
return jsonBinder.bindToRequest(request, ImmutableMap.of("rebuild", image));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,6 +25,9 @@ import static org.easymock.classextension.EasyMock.verify;
|
|||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
@ -32,7 +35,12 @@ import org.jclouds.rest.BaseRestClientTest.MockModule;
|
|||
import org.jclouds.rest.config.RestModule;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code DeltacloudRedirectionRetry}
|
||||
|
@ -41,6 +49,19 @@ import com.google.inject.Guice;
|
|||
*/
|
||||
@Test(groups = "unit")
|
||||
public class DeltacloudRedirectionRetryHandlerTest {
|
||||
Injector injector = Guice.createInjector(new MockModule(), new RestModule(), new AbstractModule() {
|
||||
@SuppressWarnings("unused")
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("CONSTANTS")
|
||||
protected Multimap<String, String> constants() {
|
||||
return LinkedHashMultimap.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
}
|
||||
});
|
||||
|
||||
@Test
|
||||
public void test302DoesNotRetryOnDelete() {
|
||||
|
@ -53,7 +74,7 @@ public class DeltacloudRedirectionRetryHandlerTest {
|
|||
|
||||
replay(command);
|
||||
|
||||
DeltacloudRedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance(
|
||||
DeltacloudRedirectionRetryHandler retry = injector.getInstance(
|
||||
DeltacloudRedirectionRetryHandler.class);
|
||||
|
||||
assert !retry.shouldRetryRequest(command, response);
|
||||
|
@ -74,7 +95,7 @@ public class DeltacloudRedirectionRetryHandlerTest {
|
|||
|
||||
replay(command);
|
||||
|
||||
DeltacloudRedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance(
|
||||
DeltacloudRedirectionRetryHandler retry = injector.getInstance(
|
||||
DeltacloudRedirectionRetryHandler.class);
|
||||
|
||||
assert !retry.shouldRetryRequest(command, response);
|
||||
|
|
|
@ -26,8 +26,11 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.encryption.internal.Base64;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.rest.MapBinder;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -39,7 +42,9 @@ import com.google.common.collect.Maps;
|
|||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class CreateServerOptions extends BindToJsonPayload {
|
||||
public class CreateServerOptions implements MapBinder {
|
||||
@Inject
|
||||
private BindToJsonPayload jsonBinder;
|
||||
|
||||
static class File {
|
||||
private final String path;
|
||||
|
@ -49,11 +54,9 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
this.path = checkNotNull(path, "path");
|
||||
this.contents = Base64.encodeBytes(checkNotNull(contents, "contents"));
|
||||
checkArgument(path.getBytes().length < 255, String.format(
|
||||
"maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path
|
||||
.getBytes().length));
|
||||
"maximum length of path is 255 bytes. Path specified %s is %d bytes", path, path.getBytes().length));
|
||||
checkArgument(contents.length < 10 * 1024, String.format(
|
||||
"maximum size of the file is 10KB. Contents specified is %d bytes",
|
||||
contents.length));
|
||||
"maximum size of the file is 10KB. Contents specified is %d bytes", contents.length));
|
||||
}
|
||||
|
||||
public String getContents() {
|
||||
|
@ -87,10 +90,9 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
|
||||
ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"),
|
||||
"name parameter not present"), checkNotNull(postParams
|
||||
.get("imageRef"), "imageRef parameter not present"), checkNotNull(
|
||||
postParams.get("flavorRef"), "flavorRef parameter not present"));
|
||||
ServerRequest server = new ServerRequest(checkNotNull(postParams.get("name"), "name parameter not present"),
|
||||
checkNotNull(postParams.get("imageRef"), "imageRef parameter not present"), checkNotNull(postParams
|
||||
.get("flavorRef"), "flavorRef parameter not present"));
|
||||
if (metadata.size() > 0)
|
||||
server.metadata = metadata;
|
||||
if (files.size() > 0)
|
||||
|
@ -128,19 +130,15 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
*/
|
||||
public CreateServerOptions withMetadata(Map<String, String> metadata) {
|
||||
checkNotNull(metadata, "metadata");
|
||||
checkArgument(metadata.size() <= 5,
|
||||
"you cannot have more then 5 metadata values. You specified: " + metadata.size());
|
||||
checkArgument(metadata.size() <= 5, "you cannot have more then 5 metadata values. You specified: "
|
||||
+ metadata.size());
|
||||
for (Entry<String, String> entry : metadata.entrySet()) {
|
||||
checkArgument(entry.getKey().getBytes().length < 255, String.format(
|
||||
"maximum length of metadata key is 255 bytes. Key specified %s is %d bytes",
|
||||
entry.getKey(), entry.getKey().getBytes().length));
|
||||
checkArgument(
|
||||
entry.getKey().getBytes().length < 255,
|
||||
String
|
||||
.format(
|
||||
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes",
|
||||
entry.getKey(), entry.getValue(),
|
||||
entry.getValue().getBytes().length));
|
||||
"maximum length of metadata key is 255 bytes. Key specified %s is %d bytes", entry.getKey(), entry
|
||||
.getKey().getBytes().length));
|
||||
checkArgument(entry.getKey().getBytes().length < 255, String.format(
|
||||
"maximum length of metadata value is 255 bytes. Value specified for %s (%s) is %d bytes", entry
|
||||
.getKey(), entry.getValue(), entry.getValue().getBytes().length));
|
||||
}
|
||||
this.metadata = metadata;
|
||||
return this;
|
||||
|
@ -164,4 +162,9 @@ public class CreateServerOptions extends BindToJsonPayload {
|
|||
return options.withMetadata(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
|
||||
return jsonBinder.bindToRequest(request, input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.rest.MapBinder;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -35,7 +38,9 @@ import com.google.common.collect.Maps;
|
|||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class RebuildServerOptions extends BindToJsonPayload {
|
||||
public class RebuildServerOptions implements MapBinder {
|
||||
@Inject
|
||||
private BindToJsonPayload jsonBinder;
|
||||
String imageRef;
|
||||
|
||||
@Override
|
||||
|
@ -43,7 +48,7 @@ public class RebuildServerOptions extends BindToJsonPayload {
|
|||
Map<String, String> image = Maps.newHashMap();
|
||||
if (imageRef != null)
|
||||
image.put("imageRef", imageRef);
|
||||
return super.bindToRequest(request, ImmutableMap.of("rebuild", image));
|
||||
return jsonBinder.bindToRequest(request, ImmutableMap.of("rebuild", image));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,7 +57,8 @@ public class RebuildServerOptions extends BindToJsonPayload {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param ref - reference of the image to rebuild the server with.
|
||||
* @param ref
|
||||
* - reference of the image to rebuild the server with.
|
||||
*/
|
||||
public RebuildServerOptions withImage(String ref) {
|
||||
checkNotNull(ref, "image reference should not be null");
|
||||
|
|
|
@ -24,14 +24,17 @@ import static org.jclouds.location.reference.LocationConstants.PROPERTY_REGIONS;
|
|||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.location.Region;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
import com.google.inject.ConfigurationException;
|
||||
import com.google.inject.Injector;
|
||||
|
@ -48,10 +51,12 @@ import com.google.inject.name.Names;
|
|||
public class ProvideRegionToURIViaProperties implements javax.inject.Provider<Map<String, URI>> {
|
||||
|
||||
private final Injector injector;
|
||||
private final Multimap<String, String> constants;
|
||||
|
||||
@Inject
|
||||
ProvideRegionToURIViaProperties(Injector injector) {
|
||||
protected ProvideRegionToURIViaProperties(Injector injector, @Named("CONSTANTS") Multimap<String, String> constants) {
|
||||
this.injector = injector;
|
||||
this.constants = constants;
|
||||
}
|
||||
|
||||
@Singleton
|
||||
|
@ -62,8 +67,13 @@ public class ProvideRegionToURIViaProperties implements javax.inject.Provider<Ma
|
|||
String regionString = injector.getInstance(Key.get(String.class, Names.named(PROPERTY_REGIONS)));
|
||||
Builder<String, URI> regions = ImmutableMap.<String, URI> builder();
|
||||
for (String region : Splitter.on(',').split(regionString)) {
|
||||
regions.put(region, URI.create(injector.getInstance(Key.get(String.class, Names.named(PROPERTY_REGION + "."
|
||||
+ region + "." + ENDPOINT)))));
|
||||
String regionUri = injector.getInstance(Key.get(String.class, Names.named(PROPERTY_REGION + "." + region
|
||||
+ "." + ENDPOINT)));
|
||||
for (Entry<String, String> entry : constants.entries()) {
|
||||
regionUri = regionUri.replace(new StringBuilder().append('{').append(entry.getKey()).append('}').toString(), entry
|
||||
.getValue());
|
||||
}
|
||||
regions.put(region, URI.create(regionUri));
|
||||
}
|
||||
return regions.build();
|
||||
} catch (ConfigurationException e) {
|
||||
|
|
|
@ -62,7 +62,6 @@ public class RegionToEndpointOrProviderIfNull implements Function<Object, URI> {
|
|||
+ ", but only the default location " + defaultProvider + " is configured");
|
||||
checkArgument(from.equals(defaultProvider) || (regionToEndpoint != null && regionToEndpoint.containsKey(from)),
|
||||
"requested location %s, which is not in the configured locations: %s", from, regionToEndpoint);
|
||||
|
||||
return regionToEndpoint.get(from);
|
||||
}
|
||||
}
|
|
@ -43,8 +43,11 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.concurrent.MoreExecutors;
|
||||
import org.jclouds.concurrent.SingleThreaded;
|
||||
|
@ -69,12 +72,16 @@ import org.jclouds.rest.internal.RestContextImpl;
|
|||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
|
@ -99,6 +106,17 @@ public class RestContextBuilder<S, A> {
|
|||
this.properties = checkNotNull(properties, "properties");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("CONSTANTS")
|
||||
protected Multimap<String, String> constants() {
|
||||
ImmutableMultimap.Builder<String, String> builder = ImmutableMultimap.<String, String> builder();
|
||||
for (Entry<Object, Object> entry : properties.entrySet())
|
||||
if (entry.getValue() != null)
|
||||
builder.put(entry.getKey().toString(), entry.getValue().toString());
|
||||
return LinkedHashMultimap.create(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
Properties toBind = new Properties();
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.rest.annotations;
|
||||
|
||||
import static java.lang.annotation.ElementType.PARAMETER;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Wraps the payload in json nested one level deep, relating to the value parameter.
|
||||
*
|
||||
* ex. "bar" becomes { "foo" :"bar" }
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Target(PARAMETER)
|
||||
@Retention(RUNTIME)
|
||||
public @interface WrapWith {
|
||||
|
||||
/**
|
||||
* what to wrap the value in
|
||||
*/
|
||||
String value();
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
package org.jclouds.rest.binders;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -36,8 +36,12 @@ import org.jclouds.rest.MapBinder;
|
|||
*/
|
||||
public class BindToJsonPayload implements MapBinder {
|
||||
|
||||
protected final Json jsonBinder;
|
||||
|
||||
@Inject
|
||||
protected Json jsonBinder;
|
||||
public BindToJsonPayload(Json jsonBinder) {
|
||||
this.jsonBinder = checkNotNull(jsonBinder, "jsonBinder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Map<String, String> postParams) {
|
||||
|
@ -46,8 +50,7 @@ public class BindToJsonPayload implements MapBinder {
|
|||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
|
||||
checkState(jsonBinder != null, "Program error: json should have been injected at this point");
|
||||
String json = jsonBinder.toJson(payload);
|
||||
String json = jsonBinder.toJson(checkNotNull(payload, "payload"));
|
||||
request.setPayload(json);
|
||||
request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_JSON);
|
||||
return request;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.rest.binders;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.rest.Binder;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
/**
|
||||
* Sometimes, cloud apis wrap requests inside an envelope. This addresses this.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class BindToJsonPayloadWrappedWith implements Binder {
|
||||
|
||||
public static interface Factory {
|
||||
BindToJsonPayloadWrappedWith create(String envelope);
|
||||
}
|
||||
|
||||
private final BindToJsonPayload jsonBinder;
|
||||
private final String envelope;
|
||||
|
||||
@Inject
|
||||
BindToJsonPayloadWrappedWith(BindToJsonPayload jsonBinder, @Assisted String envelope) {
|
||||
this.jsonBinder = checkNotNull(jsonBinder, "jsonBinder");
|
||||
this.envelope = checkNotNull(envelope, "envelope");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
|
||||
return jsonBinder.bindToRequest(request, (Object) ImmutableMap.of(envelope, checkNotNull(payload, "payload")));
|
||||
}
|
||||
|
||||
}
|
|
@ -36,8 +36,10 @@ import org.jclouds.json.config.GsonModule;
|
|||
import org.jclouds.rest.AsyncClientFactory;
|
||||
import org.jclouds.rest.HttpAsyncClient;
|
||||
import org.jclouds.rest.HttpClient;
|
||||
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
||||
import org.jclouds.rest.internal.AsyncRestClientProxy;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.jclouds.rest.internal.SeedAnnotationCache;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -49,6 +51,7 @@ import com.google.inject.Key;
|
|||
import com.google.inject.Provides;
|
||||
import com.google.inject.Scopes;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
import com.google.inject.name.Names;
|
||||
import com.google.inject.util.Types;
|
||||
import com.sun.jersey.api.uri.UriBuilderImpl;
|
||||
|
@ -63,12 +66,19 @@ public class RestModule extends AbstractModule {
|
|||
protected void configure() {
|
||||
install(new SaxParserModule());
|
||||
install(new GsonModule());
|
||||
install(new FactoryModuleBuilder().build(BindToJsonPayloadWrappedWith.Factory.class));
|
||||
bind(IdentityFunction.class).toInstance(IdentityFunction.INSTANCE);
|
||||
bind(UriBuilder.class).to(UriBuilderImpl.class);
|
||||
bind(AsyncRestClientProxy.Factory.class).to(Factory.class).in(Scopes.SINGLETON);
|
||||
BinderUtils.bindAsyncClient(binder(), HttpAsyncClient.class);
|
||||
BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class,
|
||||
ImmutableMap.<Class<?>, Class<?>> of(HttpClient.class, HttpAsyncClient.class));
|
||||
BinderUtils.bindClient(binder(), HttpClient.class, HttpAsyncClient.class, ImmutableMap.<Class<?>, Class<?>> of(
|
||||
HttpClient.class, HttpAsyncClient.class));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected ConcurrentMap<Class<?>, Boolean> seedAnnotationCache(SeedAnnotationCache seedAnnotationCache) {
|
||||
return new MapMaker().makeComputingMap(seedAnnotationCache);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
@ -88,19 +98,19 @@ public class RestModule extends AbstractModule {
|
|||
this.factory = factory;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings( { "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public Object apply(final ClassMethodArgs from) {
|
||||
Class clazz = from.getAsyncClass();
|
||||
TypeLiteral typeLiteral = TypeLiteral.get(clazz);
|
||||
RestAnnotationProcessor util = (RestAnnotationProcessor) injector.getInstance(Key.get(TypeLiteral.get(Types
|
||||
.newParameterizedType(RestAnnotationProcessor.class, clazz))));
|
||||
.newParameterizedType(RestAnnotationProcessor.class, clazz))));
|
||||
// cannot use child injectors due to the super coarse guice lock on
|
||||
// Singleton
|
||||
util.setCaller(from);
|
||||
ConcurrentMap<ClassMethodArgs, Object> delegateMap = injector.getInstance(Key.get(
|
||||
new TypeLiteral<ConcurrentMap<ClassMethodArgs, Object>>() {
|
||||
}, Names.named("async")));
|
||||
new TypeLiteral<ConcurrentMap<ClassMethodArgs, Object>>() {
|
||||
}, Names.named("async")));
|
||||
AsyncRestClientProxy proxy = new AsyncRestClientProxy(injector, factory, util, typeLiteral, delegateMap);
|
||||
injector.injectMembers(proxy);
|
||||
return AsyncClientFactory.create(clazz, proxy);
|
||||
|
@ -111,10 +121,9 @@ public class RestModule extends AbstractModule {
|
|||
@Inject
|
||||
private TransformingHttpCommandExecutorService executorService;
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings( { "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public TransformingHttpCommand<?> create(HttpRequest request,
|
||||
Function<HttpResponse, ?> transformer) {
|
||||
public TransformingHttpCommand<?> create(HttpRequest request, Function<HttpResponse, ?> transformer) {
|
||||
return new TransformingHttpCommandImpl(executorService, request, transformer);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,13 +23,9 @@ import static com.google.common.collect.Collections2.filter;
|
|||
import static com.google.common.collect.Iterables.concat;
|
||||
import static com.google.common.collect.Iterables.get;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static com.google.common.collect.Lists.newLinkedList;
|
||||
import static com.google.common.collect.Maps.filterValues;
|
||||
import static com.google.common.collect.Maps.newHashMap;
|
||||
import static com.google.common.collect.Maps.newLinkedHashMap;
|
||||
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;
|
||||
|
@ -48,12 +44,12 @@ import java.net.URI;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.Resource;
|
||||
|
@ -80,7 +76,6 @@ import org.jclouds.http.HttpResponse;
|
|||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.functions.ParseJson;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||
import org.jclouds.http.functions.ReleasePayloadAndReturn;
|
||||
import org.jclouds.http.functions.ReturnInputStream;
|
||||
|
@ -89,6 +84,7 @@ import org.jclouds.http.functions.ReturnTrueIf2xx;
|
|||
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
|
||||
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
|
||||
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
|
||||
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
|
||||
import org.jclouds.http.options.HttpRequestOptions;
|
||||
import org.jclouds.http.utils.ModifyRequest;
|
||||
import org.jclouds.internal.ClassMethodArgs;
|
||||
|
@ -104,7 +100,6 @@ import org.jclouds.rest.Binder;
|
|||
import org.jclouds.rest.InputParamValidator;
|
||||
import org.jclouds.rest.InvocationContext;
|
||||
import org.jclouds.rest.annotations.BinderParam;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
import org.jclouds.rest.annotations.Endpoint;
|
||||
import org.jclouds.rest.annotations.EndpointParam;
|
||||
import org.jclouds.rest.annotations.ExceptionParser;
|
||||
|
@ -123,30 +118,33 @@ import org.jclouds.rest.annotations.ResponseParser;
|
|||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
import org.jclouds.rest.annotations.Unwrap;
|
||||
import org.jclouds.rest.annotations.VirtualHost;
|
||||
import org.jclouds.rest.annotations.WrapWith;
|
||||
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||
import org.jclouds.rest.binders.BindMapToStringPayload;
|
||||
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
||||
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
|
||||
import org.jclouds.util.Maps2;
|
||||
import org.jclouds.util.Strings2;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.LinkedListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.ImmutableSet.Builder;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
import com.google.inject.util.Types;
|
||||
|
||||
/**
|
||||
|
@ -161,7 +159,9 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
private final Class<T> declaring;
|
||||
|
||||
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToDecoratorParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class);
|
||||
// TODO replace with Table object
|
||||
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToBinderParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class);
|
||||
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToWrapWithAnnotation = createMethodToIndexOfParamToAnnotation(WrapWith.class);
|
||||
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class);
|
||||
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class);
|
||||
static final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class);
|
||||
|
@ -175,7 +175,7 @@ public class RestAnnotationProcessor<T> {
|
|||
static final Map<MethodKey, Method> delegationMap = newHashMap();
|
||||
|
||||
static Map<Method, Map<Integer, Set<Annotation>>> createMethodToIndexOfParamToAnnotation(
|
||||
final Class<? extends Annotation> annotation) {
|
||||
final Class<? extends Annotation> annotation) {
|
||||
return new MapMaker().makeComputingMap(new Function<Method, Map<Integer, Set<Annotation>>>() {
|
||||
public Map<Integer, Set<Annotation>> apply(final Method method) {
|
||||
return new MapMaker().makeComputingMap(new GetAnnotationsForMethodParameterIndex(method, annotation));
|
||||
|
@ -193,23 +193,18 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
public Set<Annotation> apply(final Integer index) {
|
||||
Set<Annotation> keys = new HashSet<Annotation>();
|
||||
List<Annotation> parameterAnnotations = newArrayList(method.getParameterAnnotations()[index]);
|
||||
Collection<Annotation> filtered = filter(parameterAnnotations, new Predicate<Annotation>() {
|
||||
public boolean apply(Annotation input) {
|
||||
return input.annotationType().equals(clazz);
|
||||
}
|
||||
});
|
||||
for (Annotation annotation : filtered) {
|
||||
keys.add(annotation);
|
||||
}
|
||||
return keys;
|
||||
return ImmutableSet.<Annotation> copyOf(filter(ImmutableList.copyOf(method.getParameterAnnotations()[index]),
|
||||
new Predicate<Annotation>() {
|
||||
public boolean apply(Annotation input) {
|
||||
return input.annotationType().equals(clazz);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final Class<? extends HttpRequestOptions[]> optionsVarArgsClass = new HttpRequestOptions[] {}
|
||||
.getClass();
|
||||
.getClass();
|
||||
|
||||
private static final Function<? super Entry<String, String>, ? extends Part> ENTRY_TO_PART = new Function<Entry<String, String>, Part>() {
|
||||
|
||||
|
@ -220,24 +215,24 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
};
|
||||
|
||||
private final Map<Method, Set<Integer>> methodToIndexesOfOptions = new MapMaker()
|
||||
.makeComputingMap(new Function<Method, Set<Integer>>() {
|
||||
public Set<Integer> apply(final Method method) {
|
||||
Set<Integer> toReturn = newHashSet();
|
||||
for (int index = 0; index < method.getParameterTypes().length; index++) {
|
||||
Class<?> type = method.getParameterTypes()[index];
|
||||
if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type))
|
||||
toReturn.add(index);
|
||||
static final Map<Method, Set<Integer>> methodToIndexesOfOptions = new MapMaker()
|
||||
.makeComputingMap(new Function<Method, Set<Integer>>() {
|
||||
public Set<Integer> apply(final Method method) {
|
||||
Builder<Integer> toReturn = ImmutableSet.<Integer> builder();
|
||||
for (int index = 0; index < method.getParameterTypes().length; index++) {
|
||||
Class<?> type = method.getParameterTypes()[index];
|
||||
if (HttpRequestOptions.class.isAssignableFrom(type) || optionsVarArgsClass.isAssignableFrom(type))
|
||||
toReturn.add(index);
|
||||
}
|
||||
return toReturn.build();
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
private final ParseSax.Factory parserFactory;
|
||||
private final HttpUtils utils;
|
||||
private final Provider<UriBuilder> uriBuilderProvider;
|
||||
private final ConcurrentMap<Class<?>, Boolean> seedAnnotationCache;
|
||||
private final String apiVersion;
|
||||
|
||||
private char[] skips;
|
||||
|
||||
@Inject
|
||||
|
@ -250,7 +245,7 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
@VisibleForTesting
|
||||
public static Function<HttpResponse, ?> createResponseParser(ParseSax.Factory parserFactory, Injector injector,
|
||||
Method method, HttpRequest request) {
|
||||
Method method, HttpRequest request) {
|
||||
Function<HttpResponse, ?> transformer;
|
||||
Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(method);
|
||||
if (handler != null) {
|
||||
|
@ -271,7 +266,7 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
@VisibleForTesting
|
||||
public static Function<Exception, ?> createExceptionParserOrThrowResourceNotFoundOn404IfNoAnnotation(
|
||||
Injector injector, Method method) {
|
||||
Injector injector, Method method) {
|
||||
ExceptionParser annotation = method.getAnnotation(ExceptionParser.class);
|
||||
if (annotation != null) {
|
||||
return injector.getInstance(annotation.value());
|
||||
|
@ -281,60 +276,28 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Inject
|
||||
public RestAnnotationProcessor(Injector injector, ParseSax.Factory parserFactory, HttpUtils utils,
|
||||
TypeLiteral<T> typeLiteral) {
|
||||
public RestAnnotationProcessor(Injector injector, ConcurrentMap<Class<?>, Boolean> seedAnnotationCache,
|
||||
@Named(Constants.PROPERTY_API_VERSION) String apiVersion, ParseSax.Factory parserFactory, HttpUtils utils,
|
||||
TypeLiteral<T> typeLiteral) {
|
||||
this.declaring = (Class<T>) typeLiteral.getRawType();
|
||||
this.injector = injector;
|
||||
this.parserFactory = parserFactory;
|
||||
this.utils = utils;
|
||||
this.uriBuilderProvider = injector.getProvider(UriBuilder.class);
|
||||
seedCache(declaring);
|
||||
this.seedAnnotationCache = seedAnnotationCache;
|
||||
seedAnnotationCache.get(declaring);
|
||||
if (declaring.isAnnotationPresent(SkipEncoding.class)) {
|
||||
skips = declaring.getAnnotation(SkipEncoding.class).value();
|
||||
} else {
|
||||
skips = new char[] {};
|
||||
}
|
||||
this.apiVersion = injector.getInstance(Key.get(String.class, Names.named(Constants.PROPERTY_API_VERSION)));
|
||||
this.apiVersion = apiVersion;
|
||||
}
|
||||
|
||||
public Method getDelegateOrNull(Method in) {
|
||||
return delegationMap.get(new MethodKey(in));
|
||||
}
|
||||
|
||||
private void seedCache(Class<?> declaring) {
|
||||
Set<Method> methods = newHashSet(declaring.getMethods());
|
||||
methods = difference(methods, newHashSet(Object.class.getMethods()));
|
||||
for (Method method : methods) {
|
||||
if (isHttpMethod(method)) {
|
||||
for (int index = 0; index < method.getParameterTypes().length; index++) {
|
||||
methodToIndexOfParamToDecoratorParamAnnotation.get(method).get(index);
|
||||
methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToMatrixParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToFormParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToQueryParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToEndpointAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToEndpointParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPathParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPostParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToParamParserAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPartParamAnnotations.get(method).get(index);
|
||||
methodToIndexesOfOptions.get(method);
|
||||
}
|
||||
delegationMap.put(new MethodKey(method), method);
|
||||
} else if (isConstantDeclaration(method)) {
|
||||
bindConstant(method);
|
||||
} else if (!method.getDeclaringClass().equals(declaring)) {
|
||||
logger.trace("skipping potentially overridden method %s", method);
|
||||
} else if (method.isAnnotationPresent(Delegate.class)) {
|
||||
logger.trace("skipping delegate method %s", method);
|
||||
} else if (method.isAnnotationPresent(Provides.class)) {
|
||||
logger.trace("skipping provider method %s", method);
|
||||
} else {
|
||||
logger.trace("Method is not annotated as either http or constant: %s", method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MethodKey {
|
||||
|
||||
@Override
|
||||
|
@ -389,7 +352,7 @@ public class RestAnnotationProcessor<T> {
|
|||
private URI callerEndpoint;
|
||||
|
||||
public void setCaller(ClassMethodArgs caller) {
|
||||
seedCache(caller.getMethod().getDeclaringClass());
|
||||
seedAnnotationCache.get(caller.getMethod().getDeclaringClass());
|
||||
this.caller = caller;
|
||||
try {
|
||||
callerEndpoint = getEndpointFor(caller.getMethod(), caller.getArgs(), injector);
|
||||
|
@ -400,7 +363,7 @@ public class RestAnnotationProcessor<T> {
|
|||
public GeneratedHttpRequest<T> createRequest(Method method, Object... args) {
|
||||
inputParamValidator.validateMethodParametersOrThrow(method, args);
|
||||
ClassMethodArgs cma = logger.isTraceEnabled() ? new ClassMethodArgs(method.getDeclaringClass(), method, args)
|
||||
: null;
|
||||
: null;
|
||||
|
||||
URI endpoint = callerEndpoint;
|
||||
try {
|
||||
|
@ -488,7 +451,7 @@ public class RestAnnotationProcessor<T> {
|
|||
requestBuilder.headers(filterOutContentHeaders(headers));
|
||||
|
||||
try {
|
||||
requestBuilder.endpoint(builder.buildFromEncodedMap(convertUnsafe(tokenValues)));
|
||||
requestBuilder.endpoint(builder.buildFromEncodedMap(Maps2.convertUnsafe(tokenValues)));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (UriBuilderException e) {
|
||||
|
@ -560,14 +523,14 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
public static URI replaceQuery(Provider<UriBuilder> uriBuilderProvider, URI in, String newQuery,
|
||||
@Nullable Comparator<Entry<String, String>> sorter, char... skips) {
|
||||
@Nullable Comparator<Entry<String, String>> sorter, char... skips) {
|
||||
UriBuilder builder = uriBuilderProvider.get().uri(in);
|
||||
builder.replaceQuery(ModifyRequest.makeQueryLine(ModifyRequest.parseQueryToMap(newQuery), sorter, skips));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Multimap<String, String> addMatrixParams(Collection<Entry<String, String>> tokenValues, Method method,
|
||||
Object... args) {
|
||||
Object... args) {
|
||||
Multimap<String, String> matrixMap = LinkedListMultimap.create();
|
||||
if (declaring.isAnnotationPresent(MatrixParams.class)) {
|
||||
MatrixParams matrix = declaring.getAnnotation(MatrixParams.class);
|
||||
|
@ -586,7 +549,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
private Multimap<String, String> addFormParams(Collection<Entry<String, String>> tokenValues, Method method,
|
||||
Object... args) {
|
||||
Object... args) {
|
||||
Multimap<String, String> formMap = LinkedListMultimap.create();
|
||||
if (declaring.isAnnotationPresent(FormParams.class)) {
|
||||
FormParams form = declaring.getAnnotation(FormParams.class);
|
||||
|
@ -605,7 +568,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
private Multimap<String, String> addQueryParams(Collection<Entry<String, String>> tokenValues, Method method,
|
||||
Object... args) {
|
||||
Object... args) {
|
||||
Multimap<String, String> queryMap = LinkedListMultimap.create();
|
||||
if (declaring.isAnnotationPresent(QueryParams.class)) {
|
||||
QueryParams query = declaring.getAnnotation(QueryParams.class);
|
||||
|
@ -624,7 +587,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
private void addForm(Multimap<String, String> formParams, FormParams form,
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
for (int i = 0; i < form.keys().length; i++) {
|
||||
if (form.values()[i].equals(FormParams.NULL)) {
|
||||
formParams.removeAll(form.keys()[i]);
|
||||
|
@ -636,7 +599,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
private void addQuery(Multimap<String, String> queryParams, QueryParams query,
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
for (int i = 0; i < query.keys().length; i++) {
|
||||
if (query.values()[i].equals(QueryParams.NULL)) {
|
||||
queryParams.removeAll(query.keys()[i]);
|
||||
|
@ -648,7 +611,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
private void addMatrix(Multimap<String, String> matrixParams, MatrixParams matrix,
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
for (int i = 0; i < matrix.keys().length; i++) {
|
||||
if (matrix.values()[i].equals(MatrixParams.NULL)) {
|
||||
matrixParams.removeAll(matrix.keys()[i]);
|
||||
|
@ -660,7 +623,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
private void addMapPayload(Map<String, String> postParams, PayloadParams mapDefaults,
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
for (int i = 0; i < mapDefaults.keys().length; i++) {
|
||||
if (mapDefaults.values()[i].equals(PayloadParams.NULL)) {
|
||||
postParams.put(mapDefaults.keys()[i], null);
|
||||
|
@ -695,7 +658,7 @@ public class RestAnnotationProcessor<T> {
|
|||
@VisibleForTesting
|
||||
public static URI getEndpointInParametersOrNull(Method method, final Object[] args, Injector injector) {
|
||||
Map<Integer, Set<Annotation>> map = indexWithAtLeastOneAnnotation(method,
|
||||
methodToIndexOfParamToEndpointParamAnnotations);
|
||||
methodToIndexOfParamToEndpointParamAnnotations);
|
||||
if (map.size() >= 1 && args.length > 0) {
|
||||
EndpointParam firstAnnotation = (EndpointParam) get(get(map.values(), 0), 0);
|
||||
Function<Object, URI> parser = injector.getInstance(firstAnnotation.parser());
|
||||
|
@ -704,8 +667,8 @@ public class RestAnnotationProcessor<T> {
|
|||
int index = map.keySet().iterator().next();
|
||||
try {
|
||||
URI returnVal = parser.apply(args[index]);
|
||||
checkArgument(returnVal != null,
|
||||
String.format("endpoint for [%s] not configured for %s", args[index], method));
|
||||
checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", args[index],
|
||||
method));
|
||||
return returnVal;
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException(String.format("argument at index %d on method %s", index, method), e);
|
||||
|
@ -722,12 +685,12 @@ public class RestAnnotationProcessor<T> {
|
|||
});
|
||||
try {
|
||||
URI returnVal = parser.apply(argsToParse);
|
||||
checkArgument(returnVal != null,
|
||||
String.format("endpoint for [%s] not configured for %s", argsToParse, method));
|
||||
checkArgument(returnVal != null, String.format("endpoint for [%s] not configured for %s", argsToParse,
|
||||
method));
|
||||
return returnVal;
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException(String.format("argument at indexes %s on method %s", map.keySet(),
|
||||
method), e);
|
||||
method), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -763,21 +726,21 @@ public class RestAnnotationProcessor<T> {
|
|||
public static final TypeLiteral<ListenableFuture<HttpResponse>> futureHttpResponseLiteral = new TypeLiteral<ListenableFuture<HttpResponse>>() {
|
||||
};
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings( { "unchecked", "rawtypes" })
|
||||
public static Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Method method) {
|
||||
ResponseParser annotation = method.getAnnotation(ResponseParser.class);
|
||||
if (annotation == null) {
|
||||
if (method.getReturnType().equals(void.class)
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) {
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) {
|
||||
return Key.get(ReleasePayloadAndReturn.class);
|
||||
} else if (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class)
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) {
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureBooleanLiteral)) {
|
||||
return Key.get(ReturnTrueIf2xx.class);
|
||||
} else if (method.getReturnType().equals(InputStream.class)
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) {
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureInputStreamLiteral)) {
|
||||
return Key.get(ReturnInputStream.class);
|
||||
} else if (method.getReturnType().equals(HttpResponse.class)
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) {
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureHttpResponseLiteral)) {
|
||||
return Key.get((Class) IdentityFunction.class);
|
||||
} else if (getAcceptHeadersOrNull(method).contains(MediaType.APPLICATION_JSON)) {
|
||||
Type returnVal;
|
||||
|
@ -803,16 +766,16 @@ public class RestAnnotationProcessor<T> {
|
|||
parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal);
|
||||
else
|
||||
throw new IllegalStateException(String.format(
|
||||
"depth(%d) edgeCollection(%s) not yet supported for @Unwrap", depth, edgeCollection));
|
||||
"depth(%d) edgeCollection(%s) not yet supported for @Unwrap", depth, edgeCollection));
|
||||
} else {
|
||||
parserType = Types.newParameterizedType(ParseJson.class, returnVal);
|
||||
}
|
||||
return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
|
||||
} else if (method.getReturnType().equals(String.class)
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) {
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) {
|
||||
return Key.get(ReturnStringIf2xx.class);
|
||||
} else if (method.getReturnType().equals(URI.class)
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) {
|
||||
|| TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) {
|
||||
return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
|
||||
} else {
|
||||
throw new IllegalStateException("You must specify a ResponseParser annotation on: " + method.toString());
|
||||
|
@ -844,7 +807,7 @@ public class RestAnnotationProcessor<T> {
|
|||
} else {
|
||||
if (postBinders[0] instanceof org.jclouds.rest.MapBinder) {
|
||||
throw new IllegalArgumentException("we currently do not support multiple varargs postBinders in: "
|
||||
+ method.getName());
|
||||
+ method.getName());
|
||||
}
|
||||
}
|
||||
} else if (arg instanceof org.jclouds.rest.MapBinder) {
|
||||
|
@ -862,41 +825,23 @@ public class RestAnnotationProcessor<T> {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Multimap<String, String> constants = LinkedHashMultimap.create();
|
||||
|
||||
public boolean isHttpMethod(Method method) {
|
||||
return method.isAnnotationPresent(Path.class) || getHttpMethods(method) != null
|
||||
|| Sets.newHashSet(method.getParameterTypes()).contains(HttpRequest.class);
|
||||
}
|
||||
|
||||
public boolean isConstantDeclaration(Method method) {
|
||||
return method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(Named.class);
|
||||
}
|
||||
|
||||
public void bindConstant(Method method) {
|
||||
String key = method.getAnnotation(PathParam.class).value();
|
||||
String value = injector.getInstance(Key.get(String.class, method.getAnnotation(Named.class)));
|
||||
constants.put(key, value);
|
||||
}
|
||||
|
||||
public static Set<String> getHttpMethods(Method method) {
|
||||
HashSet<String> methods = new HashSet<String>();
|
||||
Builder<String> methodsBuilder = ImmutableSet.<String> builder();
|
||||
for (Annotation annotation : method.getAnnotations()) {
|
||||
HttpMethod http = annotation.annotationType().getAnnotation(HttpMethod.class);
|
||||
if (http != null)
|
||||
methods.add(http.value());
|
||||
methodsBuilder.add(http.value());
|
||||
}
|
||||
if (methods.size() == 0)
|
||||
return null;
|
||||
return methods;
|
||||
Set<String> methods = methodsBuilder.build();
|
||||
return (methods.size() == 0) ? null : methods;
|
||||
}
|
||||
|
||||
public String getHttpMethodOrConstantOrThrowException(Method method) {
|
||||
Set<String> requests = getHttpMethods(method);
|
||||
if (requests == null || requests.size() != 1) {
|
||||
throw new IllegalStateException(
|
||||
"You must use at least one, but no more than one http method or pathparam annotation on: "
|
||||
+ method.toString());
|
||||
"You must use at least one, but no more than one http method or pathparam annotation on: "
|
||||
+ method.toString());
|
||||
}
|
||||
return requests.iterator().next();
|
||||
}
|
||||
|
@ -908,17 +853,25 @@ public class RestAnnotationProcessor<T> {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static final Predicate<Set<?>> notEmpty = new Predicate<Set<?>>() {
|
||||
public boolean apply(Set<?> input) {
|
||||
return input.size() >= 1;
|
||||
}
|
||||
};
|
||||
|
||||
public GeneratedHttpRequest<T> decorateRequest(GeneratedHttpRequest<T> request) {
|
||||
OUTER: for (Entry<Integer, Set<Annotation>> entry : filterValues(
|
||||
methodToIndexOfParamToDecoratorParamAnnotation.get(request.getJavaMethod()),
|
||||
new Predicate<Set<Annotation>>() {
|
||||
public boolean apply(Set<Annotation> input) {
|
||||
return input.size() >= 1;
|
||||
}
|
||||
}).entrySet()) {
|
||||
OUTER: for (Entry<Integer, Set<Annotation>> entry : concat(//
|
||||
filterValues(methodToIndexOfParamToBinderParamAnnotation.get(request.getJavaMethod()), notEmpty)
|
||||
.entrySet(), //
|
||||
filterValues(methodToIndexOfParamToWrapWithAnnotation.get(request.getJavaMethod()), notEmpty).entrySet())) {
|
||||
boolean shouldBreak = false;
|
||||
BinderParam payloadAnnotation = (BinderParam) entry.getValue().iterator().next();
|
||||
Binder binder = injector.getInstance(payloadAnnotation.value());
|
||||
Annotation annotation = Iterables.get(entry.getValue(), 0);
|
||||
Binder binder;
|
||||
if (annotation instanceof BinderParam)
|
||||
binder = injector.getInstance(BinderParam.class.cast(annotation).value());
|
||||
else
|
||||
binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create(
|
||||
WrapWith.class.cast(annotation).value());
|
||||
if (request.getArgs().size() >= entry.getKey() + 1 && request.getArgs().get(entry.getKey()) != null) {
|
||||
Object input;
|
||||
Class<?> parameterType = request.getJavaMethod().getParameterTypes()[entry.getKey()];
|
||||
|
@ -951,24 +904,24 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
public static Map<Integer, Set<Annotation>> indexWithOnlyOneAnnotation(Method method, String description,
|
||||
Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
|
||||
Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
|
||||
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = indexWithAtLeastOneAnnotation(method, toRefine);
|
||||
if (indexToPayloadAnnotation.size() > 1) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"You must not specify more than one %s annotation on: %s; found %s", description, method.toString(),
|
||||
indexToPayloadAnnotation));
|
||||
"You must not specify more than one %s annotation on: %s; found %s", description, method.toString(),
|
||||
indexToPayloadAnnotation));
|
||||
}
|
||||
return indexToPayloadAnnotation;
|
||||
}
|
||||
|
||||
private static Map<Integer, Set<Annotation>> indexWithAtLeastOneAnnotation(Method method,
|
||||
Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
|
||||
Map<Method, Map<Integer, Set<Annotation>>> toRefine) {
|
||||
Map<Integer, Set<Annotation>> indexToPayloadAnnotation = filterValues(toRefine.get(method),
|
||||
new Predicate<Set<Annotation>>() {
|
||||
public boolean apply(Set<Annotation> input) {
|
||||
return input.size() == 1;
|
||||
}
|
||||
});
|
||||
new Predicate<Set<Annotation>>() {
|
||||
public boolean apply(Set<Annotation> input) {
|
||||
return input.size() == 1;
|
||||
}
|
||||
});
|
||||
return indexToPayloadAnnotation;
|
||||
}
|
||||
|
||||
|
@ -987,7 +940,7 @@ public class RestAnnotationProcessor<T> {
|
|||
} else {
|
||||
if (options[0] instanceof HttpRequestOptions) {
|
||||
throw new IllegalArgumentException("we currently do not support multiple varargs options in: "
|
||||
+ method.getName());
|
||||
+ method.getName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -999,7 +952,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
public Multimap<String, String> buildHeaders(Collection<Entry<String, String>> tokenValues, Method method,
|
||||
final Object... args) {
|
||||
final Object... args) {
|
||||
Multimap<String, String> headers = LinkedHashMultimap.create();
|
||||
addHeaderIfAnnotationPresentOnMethod(headers, method, tokenValues);
|
||||
Map<Integer, Set<Annotation>> indexToHeaderParam = methodToIndexOfParamToHeaderParamAnnotations.get(method);
|
||||
|
@ -1046,7 +999,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
public void addHeaderIfAnnotationPresentOnMethod(Multimap<String, String> headers, Method method,
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
if (declaring.isAnnotationPresent(Headers.class)) {
|
||||
Headers header = declaring.getAnnotation(Headers.class);
|
||||
addHeader(headers, header, tokenValues);
|
||||
|
@ -1058,7 +1011,7 @@ public class RestAnnotationProcessor<T> {
|
|||
}
|
||||
|
||||
private void addHeader(Multimap<String, String> headers, Headers header,
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
Collection<Entry<String, String>> tokenValues) {
|
||||
for (int i = 0; i < header.keys().length; i++) {
|
||||
String value = header.values()[i];
|
||||
value = Strings2.replaceTokens(value, tokenValues);
|
||||
|
@ -1067,14 +1020,6 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
}
|
||||
|
||||
private Map<String, String> convertUnsafe(Multimap<String, String> in) {
|
||||
Map<String, String> out = newLinkedHashMap();
|
||||
for (Entry<String, String> entry : in.entries()) {
|
||||
out.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
List<? extends Part> getParts(Method method, Object[] args, Iterable<Entry<String, String>> iterable) {
|
||||
List<Part> parts = newLinkedList();
|
||||
Map<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations.get(method);
|
||||
|
@ -1115,7 +1060,6 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
private Multimap<String, String> getPathParamKeyValues(Method method, Object... args) {
|
||||
Multimap<String, String> pathParamValues = LinkedHashMultimap.create();
|
||||
pathParamValues.putAll(constants);
|
||||
Map<Integer, Set<Annotation>> indexToPathParam = methodToIndexOfParamToPathParamAnnotations.get(method);
|
||||
|
||||
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
||||
|
@ -1153,7 +1097,6 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
private Multimap<String, String> getMatrixParamKeyValues(Method method, Object... args) {
|
||||
Multimap<String, String> matrixParamValues = LinkedHashMultimap.create();
|
||||
matrixParamValues.putAll(constants);
|
||||
Map<Integer, Set<Annotation>> indexToMatrixParam = methodToIndexOfParamToMatrixParamAnnotations.get(method);
|
||||
|
||||
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
||||
|
@ -1183,7 +1126,6 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
private Multimap<String, String> getFormParamKeyValues(Method method, Object... args) {
|
||||
Multimap<String, String> formParamValues = LinkedHashMultimap.create();
|
||||
formParamValues.putAll(constants);
|
||||
Map<Integer, Set<Annotation>> indexToFormParam = methodToIndexOfParamToFormParamAnnotations.get(method);
|
||||
|
||||
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
||||
|
@ -1213,7 +1155,6 @@ public class RestAnnotationProcessor<T> {
|
|||
|
||||
private Multimap<String, String> getQueryParamKeyValues(Method method, Object... args) {
|
||||
Multimap<String, String> queryParamValues = LinkedHashMultimap.create();
|
||||
queryParamValues.putAll(constants);
|
||||
Map<Integer, Set<Annotation>> indexToQueryParam = methodToIndexOfParamToQueryParamAnnotations.get(method);
|
||||
|
||||
Map<Integer, Set<Annotation>> indexToParamExtractor = methodToIndexOfParamToParamParserAnnotations.get(method);
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.rest.internal;
|
||||
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.delegationMap;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.getHttpMethods;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToBinderParamAnnotation;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToEndpointAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToEndpointParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToFormParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToHeaderParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToMatrixParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToParamParserAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPartParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPathParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToPostParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToQueryParamAnnotations;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexOfParamToWrapWithAnnotation;
|
||||
import static org.jclouds.rest.internal.RestAnnotationProcessor.methodToIndexesOfOptions;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.rest.annotations.Delegate;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor.MethodKey;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
/**
|
||||
* seeds the annotation cache
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
|
||||
@Singleton
|
||||
public class SeedAnnotationCache implements Function<Class<?>, Boolean> {
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
protected final Multimap<String, String> constants;
|
||||
protected final Injector injector;
|
||||
|
||||
@Inject
|
||||
public SeedAnnotationCache(Injector injector, @Named("CONSTANTS") Multimap<String, String> constants) {
|
||||
this.injector = injector;
|
||||
this.constants = constants;
|
||||
}
|
||||
|
||||
public void bindConstant(Method method) {
|
||||
String key = method.getAnnotation(PathParam.class).value();
|
||||
String value = injector.getInstance(Key.get(String.class, method.getAnnotation(Named.class)));
|
||||
constants.put(key, value);
|
||||
}
|
||||
|
||||
public Boolean apply(Class<?> declaring) {
|
||||
for (Method method : difference(ImmutableSet.copyOf(declaring.getMethods()), ImmutableSet.copyOf(Object.class
|
||||
.getMethods()))) {
|
||||
if (isHttpMethod(method) || method.isAnnotationPresent(Delegate.class)) {
|
||||
for (int index = 0; index < method.getParameterTypes().length; index++) {
|
||||
methodToIndexOfParamToBinderParamAnnotation.get(method).get(index);
|
||||
methodToIndexOfParamToWrapWithAnnotation.get(method).get(index);
|
||||
methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToMatrixParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToFormParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToQueryParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToEndpointAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToEndpointParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPathParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPostParamAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToParamParserAnnotations.get(method).get(index);
|
||||
methodToIndexOfParamToPartParamAnnotations.get(method).get(index);
|
||||
methodToIndexesOfOptions.get(method);
|
||||
}
|
||||
delegationMap.put(new MethodKey(method), method);
|
||||
} else if (isConstantDeclaration(method)) {
|
||||
bindConstant(method);
|
||||
} else if (!method.getDeclaringClass().equals(declaring)) {
|
||||
logger.trace("skipping potentially overridden method %s", method);
|
||||
} else if (method.isAnnotationPresent(Provides.class)) {
|
||||
logger.trace("skipping provider method %s", method);
|
||||
} else {
|
||||
logger.trace("Method is not annotated as either http or constant: %s", method);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isHttpMethod(Method method) {
|
||||
return method.isAnnotationPresent(Path.class) || getHttpMethods(method) != null
|
||||
|| ImmutableSet.copyOf(method.getParameterTypes()).contains(HttpRequest.class);
|
||||
}
|
||||
|
||||
public static boolean isConstantDeclaration(Method method) {
|
||||
return method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(Named.class);
|
||||
}
|
||||
}
|
|
@ -23,14 +23,16 @@ import static com.google.common.base.Predicates.equalTo;
|
|||
import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.collect.Maps.filterKeys;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
|
||||
/**
|
||||
* General utilities used in jclouds code for {@link Map}s.
|
||||
|
@ -39,9 +41,17 @@ import com.google.common.collect.Maps;
|
|||
*/
|
||||
public class Maps2 {
|
||||
|
||||
public static <K, V> Map<K, V> convertUnsafe(Multimap<K, V> in) {
|
||||
LinkedHashMap<K, V> out = Maps.newLinkedHashMap();
|
||||
for (Entry<K, V> entry : in.entries()) {
|
||||
out.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return ImmutableMap.copyOf(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the supplied map contains the key {@code k1}, its value will be assigned to the key
|
||||
* {@code k2}. Note that this doesn't modify the input map.
|
||||
* If the supplied map contains the key {@code k1}, its value will be assigned to the key {@code
|
||||
* k2}. Note that this doesn't modify the input map.
|
||||
*
|
||||
* @param <V>
|
||||
* type of value the map holds
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.http.handlers;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.reportMatcher;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.easymock.IArgumentMatcher;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "unit" })
|
||||
public abstract class BaseHttpErrorHandlerTest<T extends HttpErrorHandler> {
|
||||
abstract protected Class<T> getClassToTest();
|
||||
|
||||
protected void assertCodeMakes(String method, URI uri, int statusCode, String message, String content,
|
||||
Class<? extends Exception> expected, String exceptionMessage) {
|
||||
assertCodeMakes(method, uri, statusCode, message, "text/xml", content, expected, exceptionMessage);
|
||||
}
|
||||
|
||||
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
|
||||
String content, Class<? extends Exception> expected, String exceptionMessage) {
|
||||
|
||||
T function = Guice.createInjector().getInstance(getClassToTest());
|
||||
|
||||
HttpCommand command = createMock(HttpCommand.class);
|
||||
HttpRequest request = new HttpRequest(method, uri);
|
||||
HttpResponse response = new HttpResponse(statusCode, message, Payloads.newInputStreamPayload(Strings2
|
||||
.toInputStream(content)));
|
||||
response.getPayload().getContentMetadata().setContentType(contentType);
|
||||
|
||||
expect(command.getCurrentRequest()).andReturn(request).atLeastOnce();
|
||||
command.setException(exceptionEq(expected, exceptionMessage));
|
||||
|
||||
replay(command);
|
||||
|
||||
function.handleError(command, response);
|
||||
|
||||
verify(command);
|
||||
}
|
||||
|
||||
public static Exception exceptionEq(final Class<? extends Exception> in, final String exceptionMessage) {
|
||||
reportMatcher(new IArgumentMatcher() {
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("exceptionEq(");
|
||||
buffer.append(in);
|
||||
buffer.append(",");
|
||||
buffer.append(exceptionMessage);
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object arg) {
|
||||
return arg.getClass() == in && exceptionMessage.equals(Exception.class.cast(arg).getMessage());
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -25,6 +25,8 @@ import static org.easymock.classextension.EasyMock.verify;
|
|||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.jclouds.http.HttpCommand;
|
||||
|
@ -35,7 +37,12 @@ import org.jclouds.rest.config.RestModule;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code RedirectionRetryHandler}
|
||||
|
@ -44,6 +51,19 @@ import com.google.inject.Guice;
|
|||
*/
|
||||
@Test(groups = "unit")
|
||||
public class RedirectionRetryHandlerTest {
|
||||
Injector injector = Guice.createInjector(new MockModule(), new RestModule(), new AbstractModule() {
|
||||
@SuppressWarnings("unused")
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("CONSTANTS")
|
||||
protected Multimap<String, String> constants() {
|
||||
return LinkedHashMultimap.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
}
|
||||
});
|
||||
|
||||
@Test
|
||||
public void test302DoesNotRetry() {
|
||||
|
@ -55,8 +75,7 @@ public class RedirectionRetryHandlerTest {
|
|||
|
||||
replay(command);
|
||||
|
||||
RedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance(
|
||||
RedirectionRetryHandler.class);
|
||||
RedirectionRetryHandler retry = injector.getInstance(RedirectionRetryHandler.class);
|
||||
|
||||
assert !retry.shouldRetryRequest(command, response);
|
||||
|
||||
|
@ -69,14 +88,13 @@ public class RedirectionRetryHandlerTest {
|
|||
|
||||
HttpCommand command = createMock(HttpCommand.class);
|
||||
HttpResponse response = new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(
|
||||
HttpHeaders.LOCATION, "/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645"));
|
||||
HttpHeaders.LOCATION, "/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645"));
|
||||
|
||||
expect(command.incrementRedirectCount()).andReturn(5);
|
||||
|
||||
replay(command);
|
||||
|
||||
RedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance(
|
||||
RedirectionRetryHandler.class);
|
||||
RedirectionRetryHandler retry = injector.getInstance(RedirectionRetryHandler.class);
|
||||
|
||||
assert !retry.shouldRetryRequest(command, response);
|
||||
|
||||
|
@ -88,66 +106,64 @@ public class RedirectionRetryHandlerTest {
|
|||
public void test302WithPathOnlyHeader() {
|
||||
|
||||
verifyRedirectRoutes(
|
||||
new HttpRequest("GET",
|
||||
URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")),
|
||||
new HttpRequest(
|
||||
"GET",
|
||||
URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")));
|
||||
new HttpRequest("GET", URI
|
||||
.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")),
|
||||
new HttpRequest(
|
||||
"GET",
|
||||
URI
|
||||
.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/Error.aspx?aspxerrorpath=/api/v0.8b-ext2.5/org.svc/1906645")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test302ToHttps() {
|
||||
|
||||
verifyRedirectRoutes(
|
||||
new HttpRequest("GET",
|
||||
URI.create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),//
|
||||
new HttpRequest("GET", URI
|
||||
.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")));
|
||||
verifyRedirectRoutes(new HttpRequest("GET", URI
|
||||
.create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),//
|
||||
new HttpRequest("GET", URI
|
||||
.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test302ToDifferentPort() {
|
||||
|
||||
verifyRedirectRoutes(
|
||||
new HttpRequest("GET",
|
||||
URI.create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645")),//
|
||||
new HttpRequest("GET", URI
|
||||
.create("http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645")));
|
||||
verifyRedirectRoutes(new HttpRequest("GET", URI
|
||||
.create("http://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645")),//
|
||||
new HttpRequest("GET", URI
|
||||
.create("http://services.enterprisecloud.terremark.com:3030/api/v0.8b-ext2.5/org/1906645")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test302WithHeader() {
|
||||
|
||||
verifyRedirectRoutes(
|
||||
new HttpRequest("GET",
|
||||
URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")), new HttpRequest(
|
||||
"GET", URI.create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")));
|
||||
verifyRedirectRoutes(new HttpRequest("GET", URI
|
||||
.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),
|
||||
new HttpRequest("GET", URI
|
||||
.create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test302WithHeaderReplacesHostHeader() {
|
||||
|
||||
verifyRedirectRoutes(
|
||||
new HttpRequest("GET",
|
||||
URI.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"),
|
||||
ImmutableMultimap.of(HttpHeaders.HOST, "services.enterprisecloud.terremark.com")),
|
||||
new HttpResponse(302, "HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),//
|
||||
new HttpRequest("GET", URI
|
||||
.create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"),
|
||||
ImmutableMultimap.of(HttpHeaders.HOST, "services1.enterprisecloud.terremark.com")));
|
||||
verifyRedirectRoutes(new HttpRequest("GET", URI
|
||||
.create("https://services.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"),
|
||||
ImmutableMultimap.of(HttpHeaders.HOST, "services.enterprisecloud.terremark.com")), new HttpResponse(302,
|
||||
"HTTP/1.1 302 Found", null, ImmutableMultimap.of(HttpHeaders.LOCATION,
|
||||
"https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645")),//
|
||||
new HttpRequest("GET", URI
|
||||
.create("https://services1.enterprisecloud.terremark.com/api/v0.8b-ext2.5/org/1906645"),
|
||||
ImmutableMultimap.of(HttpHeaders.HOST, "services1.enterprisecloud.terremark.com")));
|
||||
|
||||
}
|
||||
|
||||
|
@ -160,8 +176,7 @@ public class RedirectionRetryHandlerTest {
|
|||
|
||||
replay(command);
|
||||
|
||||
RedirectionRetryHandler retry = Guice.createInjector(new MockModule(), new RestModule()).getInstance(
|
||||
RedirectionRetryHandler.class);
|
||||
RedirectionRetryHandler retry = injector.getInstance(RedirectionRetryHandler.class);
|
||||
|
||||
assert retry.shouldRetryRequest(command, response);
|
||||
verify(command);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.json;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public abstract class BaseItemParserTest<T> extends BaseParserTest<T, T> {
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.json;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
|
||||
import org.jclouds.io.Payloads;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.util.Types;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public abstract class BaseParserTest<T, G> {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
T expects = expected();
|
||||
|
||||
Function<HttpResponse, T> parser = getParser(getInjector());
|
||||
T response = parser.apply(new HttpResponse(200, "ok", Payloads.newInputStreamPayload(getClass()
|
||||
.getResourceAsStream(resource()))));
|
||||
compare(expects, response);
|
||||
}
|
||||
|
||||
public void compare(T expects, T response) {
|
||||
assertEquals(response.toString(), expects.toString());
|
||||
}
|
||||
|
||||
protected Injector getInjector() {
|
||||
return Guice.createInjector(new GsonModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(DateAdapter.class).to(Iso8601DateAdapter.class);
|
||||
super.configure();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Function<HttpResponse, T> getParser(Injector i) {
|
||||
return (Function<HttpResponse, T>) i.getInstance(Key.get(TypeLiteral.get(
|
||||
Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, type())).getType()));
|
||||
}
|
||||
|
||||
public abstract Class<G> type();
|
||||
|
||||
public abstract String resource();
|
||||
|
||||
public abstract T expected();
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.json;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.util.Types;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public abstract class BaseSetParserTest<T> extends BaseParserTest<Set<T>, T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
// crazy stuff due to type erasure
|
||||
@Override
|
||||
protected Function<HttpResponse, Set<T>> getParser(Injector i) {
|
||||
return (Function<HttpResponse, Set<T>>) i.getInstance(Key.get(TypeLiteral.get(
|
||||
Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, Types
|
||||
.newParameterizedType(Set.class, type()))).getType()));
|
||||
}
|
||||
|
||||
public void compare(Set<T> expects, Set<T> response) {
|
||||
assertEquals(ImmutableSortedSet.copyOf(response).toString(), ImmutableSortedSet.copyOf(expects).toString());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2011 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.rest.binders;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.assistedinject.FactoryModuleBuilder;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code BindToJsonPayloadWrappedWith}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class BindToJsonPayloadWrappedWithTest {
|
||||
|
||||
Injector injector = Guice.createInjector(new GsonModule(), new FactoryModuleBuilder()
|
||||
.build(BindToJsonPayloadWrappedWith.Factory.class));
|
||||
|
||||
@Test
|
||||
public void testCorrect() throws SecurityException, NoSuchMethodException {
|
||||
BindToJsonPayloadWrappedWith binder = new BindToJsonPayloadWrappedWith(injector
|
||||
.getInstance(BindToJsonPayload.class), "envelope");
|
||||
|
||||
HttpRequest request = HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build();
|
||||
request = binder.bindToRequest(request, ImmutableMap.of("imageName", "foo", "serverId", "2"));
|
||||
assertEquals(request.getPayload().getRawContent(), "{\"envelope\":{\"imageName\":\"foo\",\"serverId\":\"2\"}}");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryCorrect() throws SecurityException, NoSuchMethodException {
|
||||
BindToJsonPayloadWrappedWith binder = injector.getInstance(BindToJsonPayloadWrappedWith.Factory.class).create(
|
||||
"envelope");
|
||||
|
||||
HttpRequest request = HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build();
|
||||
request = binder.bindToRequest(request, ImmutableMap.of("imageName", "foo", "serverId", "2"));
|
||||
assertEquals(request.getPayload().getRawContent(), "{\"envelope\":{\"imageName\":\"foo\",\"serverId\":\"2\"}}");
|
||||
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNullIsBad() {
|
||||
BindToJsonPayloadWrappedWith binder = new BindToJsonPayloadWrappedWith(injector
|
||||
.getInstance(BindToJsonPayload.class), "envelope");
|
||||
binder.bindToRequest(HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build(), null);
|
||||
}
|
||||
}
|
|
@ -132,6 +132,7 @@ import org.jclouds.rest.annotations.ResponseParser;
|
|||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
import org.jclouds.rest.annotations.Unwrap;
|
||||
import org.jclouds.rest.annotations.VirtualHost;
|
||||
import org.jclouds.rest.annotations.WrapWith;
|
||||
import org.jclouds.rest.binders.BindAsHostPrefix;
|
||||
import org.jclouds.rest.binders.BindMapToMatrixParams;
|
||||
import org.jclouds.rest.binders.BindToJsonPayload;
|
||||
|
@ -200,6 +201,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
|||
|
||||
@Delegate
|
||||
public Callee getCallee();
|
||||
|
||||
@Delegate
|
||||
public Callee getCallee(@EndpointParam URI endpoint);
|
||||
}
|
||||
|
||||
@Timeout(duration = 10, timeUnit = TimeUnit.NANOSECONDS)
|
||||
|
@ -212,6 +216,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
|||
|
||||
@Delegate
|
||||
public AsyncCallee getCallee();
|
||||
|
||||
@Delegate
|
||||
public AsyncCallee getCallee(@EndpointParam URI endpoint);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -284,6 +291,31 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
|||
|
||||
}
|
||||
|
||||
public void testDelegateWithOverridingEndpointOnMethod() throws SecurityException, NoSuchMethodException,
|
||||
InterruptedException, ExecutionException {
|
||||
Injector child = injectorForClient();
|
||||
TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class);
|
||||
|
||||
ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class);
|
||||
|
||||
try {
|
||||
child.getInstance(Callee.class);
|
||||
assert false : "Callee shouldn't be bound yet";
|
||||
} catch (ConfigurationException e) {
|
||||
|
||||
}
|
||||
|
||||
Caller caller = child.getInstance(Caller.class);
|
||||
expect(mock.submit(requestLineEquals("GET http://howdyboys/client/1/foo HTTP/1.1"), eq(function)))
|
||||
.andReturn(Futures.<Void> immediateFuture(null)).atLeastOnce();
|
||||
replay(mock);
|
||||
|
||||
caller.getCallee(URI.create("http://howdyboys")).onePath("foo");
|
||||
|
||||
verify(mock);
|
||||
|
||||
}
|
||||
|
||||
private Injector injectorForClient() {
|
||||
|
||||
RestContextSpec<Caller, AsyncCaller> contextSpec = contextSpec("test", "http://localhost:9999", "1", "",
|
||||
|
@ -768,6 +800,10 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
|||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
String testUnwrap();
|
||||
|
||||
@POST
|
||||
@Path("/")
|
||||
String testWrapWith(@WrapWith("foo") String param);
|
||||
|
||||
@GET
|
||||
@Path("/")
|
||||
@Unwrap
|
||||
|
@ -928,6 +964,12 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
|
|||
|
||||
}
|
||||
|
||||
public void testWrapWith() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = TestPut.class.getMethod("testWrapWith", String.class);
|
||||
HttpRequest request = factory(TestPut.class).createRequest(method, "bar");
|
||||
assertPayloadEquals(request, "{\"foo\":\"bar\"}", "application/json", false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUnwrap2() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = TestPut.class.getMethod("testUnwrap2");
|
||||
|
|
Loading…
Reference in New Issue