Merge pull request #1259 from jclouds/route53-rrsets

Route53 rrsets
This commit is contained in:
Adrian Cole 2013-01-28 21:14:36 -08:00
commit 42ab9c21fb
25 changed files with 1137 additions and 188 deletions

View File

@ -0,0 +1,32 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.aws.route53.features;
import org.jclouds.route53.features.ResourceRecordSetApiLiveTest;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = "live", testName = "AWSResourceRecordSetApiLiveTest")
public class AWSResourceRecordSetApiLiveTest extends ResourceRecordSetApiLiveTest {
public AWSResourceRecordSetApiLiveTest() {
provider = "aws-route53";
}
}

View File

@ -60,13 +60,13 @@
</dependency>
<dependency>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-log4j</artifactId>
<artifactId>jclouds-slf4j</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -18,8 +18,11 @@
*/
package org.jclouds.route53;
import javax.ws.rs.PathParam;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.features.ResourceRecordSetApi;
import org.jclouds.route53.features.ZoneApi;
/**
@ -47,4 +50,10 @@ public interface Route53Api {
*/
@Delegate
ZoneApi getZoneApi();
/**
* Provides synchronous access to ResourceRecordSet features.
*/
@Delegate
ResourceRecordSetApi getResourceRecordSetApiForZone(@PathParam("zoneId") String zoneId);
}

View File

@ -30,6 +30,7 @@ import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.features.ResourceRecordSetAsyncApi;
import org.jclouds.route53.features.ZoneAsyncApi;
import org.jclouds.route53.filters.RestAuthentication;
import org.jclouds.route53.xml.ChangeHandler;
@ -64,4 +65,10 @@ public interface Route53AsyncApi {
*/
@Delegate
ZoneAsyncApi getZoneApi();
/**
* Provides asynchronous access to ResourceRecordSet features.
*/
@Delegate
ResourceRecordSetAsyncApi getResourceRecordSetApiForZone(@PathParam("zoneId") String zoneId);
}

View File

@ -0,0 +1,45 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.binders;
import javax.inject.Singleton;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequest.Builder;
import org.jclouds.rest.Binder;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
/**
*
* @author Adrian Cole
*/
@Singleton
public class BindNextRecord implements Binder {
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
NextRecord from = NextRecord.class.cast(payload);
Builder<?> builder = request.toBuilder();
builder.addQueryParam("name", from.getName());
builder.addQueryParam("type", from.getType().toString());
if (from.getIdentifier().isPresent())
builder.addQueryParam("identifier", from.getIdentifier().get());
return (R) builder.build();
}
}

View File

@ -32,6 +32,8 @@ import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RequestSigner;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.Route53AsyncApi;
import org.jclouds.route53.features.ResourceRecordSetApi;
import org.jclouds.route53.features.ResourceRecordSetAsyncApi;
import org.jclouds.route53.features.ZoneApi;
import org.jclouds.route53.features.ZoneAsyncApi;
import org.jclouds.route53.filters.RestAuthentication;
@ -47,7 +49,8 @@ import com.google.inject.Provides;
@ConfiguresRestClient
public class Route53RestClientModule extends AWSRestClientModule<Route53Api, Route53AsyncApi> {
public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()//
.put(ZoneApi.class, ZoneAsyncApi.class).build();
.put(ZoneApi.class, ZoneAsyncApi.class)
.put(ResourceRecordSetApi.class, ResourceRecordSetAsyncApi.class).build();
public Route53RestClientModule() {
super(typeToken(Route53Api.class), typeToken(Route53AsyncApi.class), DELEGATE_MAP);

View File

@ -0,0 +1,167 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.domain;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ForwardingList;
import com.google.common.collect.ImmutableList;
/**
*
* @author Adrian Cole
*/
public final class ResourceRecordSet extends ForwardingList<String> {
private final String name;
private final Type type;
private final Optional<Integer> ttl;
private final ImmutableList<String> items;
private ResourceRecordSet(String name, Type type, Optional<Integer> ttl, ImmutableList<String> items) {
this.name = checkNotNull(name, "name");
this.type = checkNotNull(type, "type of %s", name);
this.ttl = checkNotNull(ttl, "ttl for %s", name);
this.items = checkNotNull(items, "items for %s", name);
}
/**
* The name of the domain.
*/
public String getName() {
return name;
}
/**
* The resource record set type.
*/
public Type getType() {
return type;
}
/**
* Present in all resource record sets except aliases. The resource record
* cache time to live (TTL), in seconds.
*/
public Optional<Integer> getTTL() {
return ttl;
}
@Override
public int hashCode() {
return Objects.hashCode(name, type, ttl, items);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ResourceRecordSet other = ResourceRecordSet.class.cast(obj);
return equal(this.name, other.name) && equal(this.type, other.type) && equal(this.ttl, other.ttl)
&& equal(this.items, other.items);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("name", name).add("type", type).add("ttl", ttl.orNull())
.add("resourceRecords", items).toString();
}
@Override
protected List<String> delegate() {
return items;
}
public enum Type {
A, AAAA, CNAME, MX, NS, PTR, SOA, SPF, SRV, TXT, UNRECOGNIZED;
public static Type fromValue(String type) {
try {
return valueOf(checkNotNull(type, "type"));
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().from(this);
}
public final static class Builder {
private String name;
private Type type;
private Optional<Integer> ttl = Optional.absent();
private ImmutableList.Builder<String> items = ImmutableList.<String> builder();
/**
* @see ResourceRecordSet#getName()
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* @see ResourceRecordSet#getType()
*/
public Builder type(Type type) {
this.type = type;
return this;
}
/**
* @see ResourceRecordSet#getTTL()
*/
public Builder ttl(Integer ttl) {
this.ttl = Optional.fromNullable(ttl);
return this;
}
public Builder add(String item) {
this.items.add(item);
return this;
}
public Builder addAll(Iterable<String> items) {
this.items.addAll(items);
return this;
}
public ResourceRecordSet build() {
return new ResourceRecordSet(name, type, ttl, items.build());
}
public Builder from(ResourceRecordSet in) {
return this.name(in.name).type(in.type).ttl(in.ttl.orNull()).addAll(in.items);
}
}
}

View File

@ -0,0 +1,196 @@
/*
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.domain;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Iterator;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
/**
*
* @author Adrian Cole
*/
public class ResourceRecordSetIterable extends IterableWithMarker<ResourceRecordSet> {
private final Iterable<ResourceRecordSet> items;
private final Optional<NextRecord> nextRecord;
private ResourceRecordSetIterable(Iterable<ResourceRecordSet> items, @Nullable NextRecord nextRecord) {
this.items = checkNotNull(items, "items");
this.nextRecord = Optional.fromNullable(nextRecord);
}
/**
* present when the list is not truncated
*/
public Optional<NextRecord> nextRecord() {
return nextRecord;
}
@SuppressWarnings("unchecked")
@Override
public Optional<Object> nextMarker() {
return Optional.class.cast(nextRecord);
}
@Override
public Iterator<ResourceRecordSet> iterator() {
return items.iterator();
}
@Override
public int hashCode() {
return Objects.hashCode(items, nextRecord);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ResourceRecordSetIterable that = ResourceRecordSetIterable.class.cast(obj);
return equal(this.items, that.items) && equal(this.nextRecord, that.nextRecord);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("items", items).add("nextRecord", nextRecord.orNull()).toString();
}
/**
* If the results were truncated, this holds the position of the next item.
*/
public static class NextRecord {
public static NextRecord nameAndType(String name, Type type) {
return new NextRecord(name, type, null);
}
public static NextRecord nameTypeAndIdentifier(String name, Type type, String identifier) {
return new NextRecord(name, type, identifier);
}
private final String name;
private final Type type;
private final Optional<String> identifier;
private NextRecord(String name, Type type, String identifier) {
this.name = checkNotNull(name, "name");
this.type = checkNotNull(type, "type for %s", name);
this.identifier = Optional.fromNullable(identifier);
}
/**
* If the results were truncated, the name of the next record in the list.
*/
public String getName() {
return name;
}
/**
* the type of the next record in the list.
*/
public Type getType() {
return type;
}
/**
* Weighted and latency resource record sets only.
*/
public Optional<String> getIdentifier() {
return identifier;
}
@Override
public int hashCode() {
return Objects.hashCode(name, type, identifier);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
NextRecord other = NextRecord.class.cast(obj);
return equal(this.name, other.name) && equal(this.type, other.type)
&& equal(this.identifier, other.identifier);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("name", name).add("type", type)
.add("identifier", identifier.orNull()).toString();
}
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private ImmutableList.Builder<ResourceRecordSet> items = ImmutableList.<ResourceRecordSet> builder();
private String nextRecordName;
private Type nextRecordType;
private String nextRecordIdentifier;
public Builder add(ResourceRecordSet item) {
this.items.add(item);
return this;
}
public Builder addAll(Iterable<ResourceRecordSet> items) {
this.items.addAll(items);
return this;
}
public Builder nextRecordName(String nextRecordName) {
this.nextRecordName = nextRecordName;
return this;
}
public Builder nextRecordType(Type nextRecordType) {
this.nextRecordType = nextRecordType;
return this;
}
public Builder nextRecordIdentifier(String nextRecordIdentifier) {
this.nextRecordIdentifier = nextRecordIdentifier;
return this;
}
public ResourceRecordSetIterable build() {
NextRecord nextRecord = nextRecordName != null ? new NextRecord(nextRecordName, nextRecordType,
nextRecordIdentifier) : null;
return new ResourceRecordSetIterable(items.build(), nextRecord);
}
}
}

View File

@ -0,0 +1,50 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.features;
import org.jclouds.collect.PagedIterable;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
/**
* @see ResourceRecordSetAsyncApi
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/ActionsOnRRS.html"
* />
* @author Adrian Cole
*/
public interface ResourceRecordSetApi {
/**
* returns all resource record sets in order.
*/
PagedIterable<ResourceRecordSet> list();
/**
* retrieves up to 100 resource record sets in order.
*/
ResourceRecordSetIterable listFirstPage();
/**
* retrieves up to 100 resource record sets in order, starting at {@code nextRecord}
*/
ResourceRecordSetIterable listAt(NextRecord nextRecord);
}

View File

@ -0,0 +1,80 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.features;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.jclouds.collect.PagedIterable;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.route53.binders.BindNextRecord;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.route53.filters.RestAuthentication;
import org.jclouds.route53.functions.ResourceRecordSetsToPagedIterable;
import org.jclouds.route53.xml.ListResourceRecordSetsResponseHandler;
import com.google.common.util.concurrent.ListenableFuture;
/**
* @see ResourceRecordSetApi
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/ActionsOnRRS.html"
* />
* @author Adrian Cole
*/
@RequestFilters(RestAuthentication.class)
@VirtualHost
@Path("/{jclouds.api-version}/hostedzone/{zoneId}")
public interface ResourceRecordSetAsyncApi {
/**
* @see ResourceRecordSetApi#list()
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
@Transform(ResourceRecordSetsToPagedIterable.class)
ListenableFuture<PagedIterable<ResourceRecordSet>> list();
/**
* @see ResourceRecordSetApi#listFirstPage
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
ListenableFuture<ResourceRecordSetIterable> listFirstPage();
/**
* @see ResourceRecordSetApi#listAt(NextRecord)
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
ListenableFuture<ResourceRecordSetIterable> listAt(@BinderParam(BindNextRecord.class) NextRecord nextRecord);
}

View File

@ -61,24 +61,19 @@ public interface ZoneApi {
NewZone createWithReferenceAndComment(String name, String callerReference, String comment);
/**
* Lists the zones that have the specified path prefix. If there are none,
* the action returns an empty list.
* The action retrieves a specified number of zones in order.
*
* <br/>
* You can paginate the results using the {@link ListZonesOptions parameter}
* You can paginate the results using the
* {@link ListZonesOptions parameter}
*
* @param options
* the options describing the zones query
*
* @return the response object
*/
IterableWithMarker<Zone> list(ListZonesOptions options);
/**
* Lists the zones that have the specified path prefix. If there are none,
* the action returns an empty list.
*
* @return the response object
* returns all zones in order.
*/
PagedIterable<Zone> list();

View File

@ -0,0 +1,65 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Inject;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.internal.CallerArg0ToPagedIterable;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.route53.features.ResourceRecordSetApi;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
/**
* @author Adrian Cole
*/
@Beta
public class ResourceRecordSetsToPagedIterable extends
CallerArg0ToPagedIterable<ResourceRecordSet, ResourceRecordSetsToPagedIterable> {
private final Route53Api api;
@Inject
protected ResourceRecordSetsToPagedIterable(Route53Api api) {
this.api = checkNotNull(api, "api");
}
@Override
protected Function<Object, IterableWithMarker<ResourceRecordSet>> markerToNextForCallingArg0(String zoneId) {
final ResourceRecordSetApi resourceRecordSetApi = api.getResourceRecordSetApiForZone(zoneId);
return new Function<Object, IterableWithMarker<ResourceRecordSet>>() {
@Override
public IterableWithMarker<ResourceRecordSet> apply(Object input) {
return resourceRecordSetApi.listAt(NextRecord.class.cast(input));
}
@Override
public String toString() {
return "listResourceRecordSets()";
}
};
}
}

View File

@ -0,0 +1,101 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.xml;
import static org.jclouds.util.SaxUtils.currentOrNull;
import static org.jclouds.util.SaxUtils.equalsOrSuffix;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.domain.ResourceRecordSetIterable.Builder;
import org.xml.sax.Attributes;
import com.google.inject.Inject;
/**
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/API_ListResourceRecordSets.html"
* />
*
* @author Adrian Cole
*/
public class ListResourceRecordSetsResponseHandler extends
ParseSax.HandlerForGeneratedRequestWithResult<ResourceRecordSetIterable> {
private final ResourceRecordSetHandler resourceRecordSetHandler;
private StringBuilder currentText = new StringBuilder();
private Builder builder = ResourceRecordSetIterable.builder();
private boolean inResourceRecordSets;
@Inject
public ListResourceRecordSetsResponseHandler(ResourceRecordSetHandler resourceRecordSetHandler) {
this.resourceRecordSetHandler = resourceRecordSetHandler;
}
@Override
public ResourceRecordSetIterable getResult() {
try {
return builder.build();
} finally {
builder = ResourceRecordSetIterable.builder();
}
}
@Override
public void startElement(String url, String name, String qName, Attributes attributes) {
if (equalsOrSuffix(qName, "ResourceRecordSets")) {
inResourceRecordSets = true;
}
if (inResourceRecordSets) {
resourceRecordSetHandler.startElement(url, name, qName, attributes);
}
}
@Override
public void endElement(String uri, String name, String qName) {
if (inResourceRecordSets) {
if (qName.equals("ResourceRecordSets")) {
inResourceRecordSets = false;
} else if (qName.equals("ResourceRecordSet")) {
builder.add(resourceRecordSetHandler.getResult());
} else {
resourceRecordSetHandler.endElement(uri, name, qName);
}
} else if (qName.equals("NextRecordName")) {
builder.nextRecordName(currentOrNull(currentText));
} else if (qName.equals("NextRecordType")) {
builder.nextRecordType(Type.valueOf(currentOrNull(currentText)));
} else if (qName.equals("NextRecordIdentifier")) {
builder.nextRecordIdentifier(currentOrNull(currentText));
}
currentText = new StringBuilder();
}
@Override
public void characters(char ch[], int start, int length) {
if (inResourceRecordSets) {
resourceRecordSetHandler.characters(ch, start, length);
} else {
currentText.append(ch, start, length);
}
}
}

View File

@ -0,0 +1,68 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.xml;
import static org.jclouds.util.SaxUtils.currentOrNull;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.xml.sax.Attributes;
/**
*
* @author Adrian Cole
*/
public class ResourceRecordSetHandler extends ParseSax.HandlerForGeneratedRequestWithResult<ResourceRecordSet> {
private StringBuilder currentText = new StringBuilder();
private ResourceRecordSet.Builder builder = ResourceRecordSet.builder();
@Override
public ResourceRecordSet getResult() {
try {
return builder.build();
} finally {
builder = ResourceRecordSet.builder();
}
}
@Override
public void startElement(String url, String name, String qName, Attributes attributes) {
}
@Override
public void endElement(String uri, String name, String qName) {
if (qName.equals("Name")) {
builder.name(currentOrNull(currentText));
} else if (qName.equals("Type")) {
builder.type(Type.valueOf(currentOrNull(currentText)));
} else if (qName.equals("TTL")) {
builder.ttl(Integer.parseInt(currentOrNull(currentText)));
} else if (qName.equals("Value")) {
builder.add(currentOrNull(currentText));
}
currentText = new StringBuilder();
}
@Override
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -49,8 +49,6 @@ public class Route53ApiExpectTest extends BaseRoute53ApiExpectTest {
assertEquals(api.getChange("C2682N5HXP0BZ4").toString(), new GetChangeResponseTest().expected().toString());
}
HttpResponse notFound = HttpResponse.builder().statusCode(404).build();
public void testGetChangeNullWhenResponseIs404() {
Route53Api api = requestSendsResponse(getChange, notFound);
assertNull(api.getChange("C2682N5HXP0BZ4"));

View File

@ -0,0 +1,88 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unles 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 expres or implied. See the License for the
* specific language governing permisions and limitations
* under the License.
*/
package org.jclouds.route53.features;
import static org.testng.Assert.assertEquals;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.route53.internal.BaseRoute53ApiExpectTest;
import org.jclouds.route53.parse.ListResourceRecordSetsResponseTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ResourceRecordSetApiExpectTest")
public class ResourceRecordSetApiExpectTest extends BaseRoute53ApiExpectTest {
HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
.build();
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/rrsets.xml", "text/xml")).build();
public void testListWhenResponseIs2xx() {
Route53Api success = requestSendsResponse(list, listResponse);
assertEquals(success.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").list().get(0).toString(), new ListResourceRecordSetsResponseTest().expected()
.toString());
}
// TODO: this should really be an empty set
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testListWhenResponseIs404() {
Route53Api fail = requestSendsResponse(list, notFound);
assertEquals(fail.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").list().get(0).toSet(), ImmutableSet.of());
}
HttpRequest listAt = HttpRequest.builder().method("GET")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset?name=testdoc2.example.com&type=NS")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
.build();
public void testListAtWhenResponseIs2xx() {
Route53Api apiWhenAtExist = requestSendsResponse(listAt, listResponse);
NextRecord next = NextRecord.nameAndType("testdoc2.example.com", Type.NS);
assertEquals(apiWhenAtExist.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").listAt(next).toString(),
new ListResourceRecordSetsResponseTest().expected().toString());
}
public void testList2PagesWhenResponseIs2xx() {
HttpResponse noMore = HttpResponse.builder().statusCode(200)
.payload(payloadFromStringWithContentType("<ListResourceRecordSetsResponse />", "text/xml")).build();
Route53Api success = requestsSendResponses(list, listResponse, listAt, noMore);
assertEquals(success.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").list().concat().toSet(), new ListResourceRecordSetsResponseTest().expected()
.toSet());
}
}

View File

@ -0,0 +1,64 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.features;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import java.util.List;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.Zone;
import org.jclouds.route53.internal.BaseRoute53ApiLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = "live", testName = "ResourceRecordSetApiLiveTest")
public class ResourceRecordSetApiLiveTest extends BaseRoute53ApiLiveTest {
private void checkResourceRecordSet(ResourceRecordSet resourceRecordSet) {
checkNotNull(resourceRecordSet.getName(), "Id cannot be null for a ResourceRecordSet %s", resourceRecordSet);
checkNotNull(resourceRecordSet.getType(), "Type cannot be null for a ResourceRecordSet %s", resourceRecordSet);
checkNotNull(resourceRecordSet.getTTL(),
"While TTL can be null for a ResourceRecordSet, its Optional wrapper cannot %s", resourceRecordSet);
}
@Test
protected void testListResourceRecordSets() {
IterableWithMarker<Zone> zones = context.getApi().getZoneApi().list().get(0);
if (zones.isEmpty())
throw new SkipException("no zones in context: " + context);
Zone zone = zones.first().get();
List<ResourceRecordSet> records = api(zone.getId()).list().concat().toList();
assertEquals(zone.getResourceRecordSetCount(), records.size());
for (ResourceRecordSet resourceRecordSet : records) {
checkResourceRecordSet(resourceRecordSet);
}
}
protected ResourceRecordSetApi api(String zoneId) {
return context.getApi().getResourceRecordSetApiForZone(zoneId);
}
}

View File

@ -93,8 +93,6 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
assertEquals(success.getZoneApi().get("Z1XTHCPEFRWV1X").toString(), new GetHostedZoneResponseTest().expected()
.toString());
}
HttpResponse notFound = HttpResponse.builder().statusCode(404).build();
public void testGetWhenResponseIs404() {
Route53Api fail = requestSendsResponse(get, notFound);

View File

@ -41,7 +41,6 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* @author Adrian Cole
@ -63,7 +62,7 @@ public class ZoneApiLiveTest extends BaseRoute53ApiLiveTest {
private void checkZone(Zone zone) {
checkNotNull(zone.getId(), "Id cannot be null for a Zone %s", zone);
checkNotNull(zone.getName(), "Id cannot be null for a Zone %s", zone);
checkNotNull(zone.getName(), "Name cannot be null for a Zone %s", zone);
checkNotNull(zone.getCallerReference(), "CallerReference cannot be null for a Zone %s", zone);
checkNotNull(zone.getComment(), "While Comment can be null for a Zone, its Optional wrapper cannot %s", zone);
}
@ -76,7 +75,7 @@ public class ZoneApiLiveTest extends BaseRoute53ApiLiveTest {
checkZone(zone);
}
if (Iterables.size(response) > 0) {
if (response.size() > 0) {
Zone zone = response.iterator().next();
assertEquals(api().get(zone.getId()).getZone(), zone);
}

View File

@ -18,6 +18,7 @@
*/
package org.jclouds.route53.internal;
import org.jclouds.http.HttpResponse;
import org.jclouds.route53.Route53Api;
/**
@ -25,5 +26,5 @@ import org.jclouds.route53.Route53Api;
* @author Adrian Cole
*/
public class BaseRoute53ApiExpectTest extends BaseRoute53ExpectTest<Route53Api> {
protected HttpResponse notFound = HttpResponse.builder().statusCode(404).build();
}

View File

@ -31,7 +31,7 @@ import com.google.common.collect.ImmutableSet;
*
* @author Adrian Cole
*/
@Test(groups = "maxItems", testName = "ListZonesOptionsTest")
@Test(groups = "unit", testName = "ListZonesOptionsTest")
public class ListZonesOptionsTest {
public void testMarker() {

View File

@ -0,0 +1,72 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.parse;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.xml.ListResourceRecordSetsResponseHandler;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
// NOTE:without testName, this will not call @Before* and fail w/NPE during
// surefire
@Test(groups = "unit", testName = "ListResourceRecordSetsResponseTest")
public class ListResourceRecordSetsResponseTest extends BaseHandlerTest {
public void test() {
InputStream is = getClass().getResourceAsStream("/rrsets.xml");
ResourceRecordSetIterable expected = expected();
ListResourceRecordSetsResponseHandler handler = injector.getInstance(ListResourceRecordSetsResponseHandler.class);
ResourceRecordSetIterable result = factory.create(handler).parse(is);
assertEquals(result.toString(), expected.toString());
}
public ResourceRecordSetIterable expected() {
return ResourceRecordSetIterable.builder()
.add(ResourceRecordSet.builder()
.name("example.com.")
.type(Type.SOA)
.ttl(900)
.add("ns-2048.awsdns-64.net. hostmaster.awsdns.com. 1 7200 900 1209600 86400")
.build())
.add(ResourceRecordSet.builder()
.name("example.com.")
.type(Type.NS)
.ttl(172800)
.add("ns-2048.awsdns-64.com.")
.add("ns-2049.awsdns-65.net.")
.add("ns-2050.awsdns-66.org.")
.add("ns-2051.awsdns-67.co.uk.")
.build())
.nextRecordName("testdoc2.example.com")
.nextRecordType(Type.NS).build();
}
}

View File

@ -1,166 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--
For more configuration infromation and examples see the Apache
Log4j website: http://logging.apache.org/log4j/
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">
<!-- A time/date based rolling appender -->
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-wire.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="BLOBSTOREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-blobstore.log" />
<param name="Append" value="true" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="COMPUTEFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-compute.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="SSHFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-ssh.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message\n <param name="ConversionPattern"
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-->
</layout>
</appender>
<appender name="ASYNCCOMPUTE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="COMPUTEFILE" />
</appender>
<appender name="ASYNCSSH" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="SSHFILE" />
</appender>
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="WIREFILE" />
</appender>
<appender name="ASYNCBLOBSTORE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="BLOBSTOREFILE" />
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.jclouds">
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
<category name="jclouds.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<category name="jclouds.ssh">
<priority value="DEBUG" />
<appender-ref ref="ASYNCSSH" />
</category>
<category name="jclouds.wire">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<category name="jclouds.blobstore">
<priority value="DEBUG" />
<appender-ref ref="ASYNCBLOBSTORE" />
</category>
<category name="jclouds.compute">
<priority value="TRACE" />
<appender-ref ref="ASYNCCOMPUTE" />
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="WARN" />
</root>
</log4j:configuration>

View File

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

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<ListResourceRecordSetsResponse
xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
<ResourceRecordSets>
<ResourceRecordSet>
<Name>example.com.</Name>
<Type>SOA</Type>
<TTL>900</TTL>
<ResourceRecords>
<ResourceRecord>
<Value>ns-2048.awsdns-64.net. hostmaster.awsdns.com. 1 7200 900 1209600 86400</Value>
</ResourceRecord>
</ResourceRecords>
</ResourceRecordSet>
<ResourceRecordSet>
<Name>example.com.</Name>
<Type>NS</Type>
<TTL>172800</TTL>
<ResourceRecords>
<ResourceRecord>
<Value>ns-2048.awsdns-64.com.</Value>
</ResourceRecord>
<ResourceRecord>
<Value>ns-2049.awsdns-65.net.</Value>
</ResourceRecord>
<ResourceRecord>
<Value>ns-2050.awsdns-66.org.</Value>
</ResourceRecord>
<ResourceRecord>
<Value>ns-2051.awsdns-67.co.uk.</Value>
</ResourceRecord>
</ResourceRecords>
</ResourceRecordSet>
</ResourceRecordSets>
<IsTruncated>true</IsTruncated>
<MaxItems>3</MaxItems>
<NextRecordName>testdoc2.example.com</NextRecordName>
<NextRecordType>NS</NextRecordType>
</ListResourceRecordSetsResponse>