diff --git a/labs/route53/src/main/java/org/jclouds/route53/Route53Api.java b/labs/route53/src/main/java/org/jclouds/route53/Route53Api.java index 1ee6bf1c61..729d870b67 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/Route53Api.java +++ b/labs/route53/src/main/java/org/jclouds/route53/Route53Api.java @@ -38,7 +38,7 @@ public interface Route53Api { * * @param changeID * The ID of the change batch request. - * @return nulll, if not found + * @return null, if not found */ Change getChange(String changeID); diff --git a/labs/route53/src/main/java/org/jclouds/route53/domain/Change.java b/labs/route53/src/main/java/org/jclouds/route53/domain/Change.java index 1b5c587e2a..a253e8553f 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/domain/Change.java +++ b/labs/route53/src/main/java/org/jclouds/route53/domain/Change.java @@ -18,6 +18,8 @@ */ 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.Date; @@ -28,51 +30,6 @@ import com.google.common.base.Objects; * @author Adrian Cole */ public final class Change { - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return builder().from(this); - } - - public final static class Builder { - private String id; - private Status status; - private Date submittedAt; - - /** - * @see Change#getId() - */ - public Builder id(String id) { - this.id = id; - return this; - } - - /** - * @see Change#getStatus() - */ - public Builder status(Status status) { - this.status = status; - return this; - } - - /** - * @see Change#getSubmittedAt() - */ - public Builder submittedAt(Date submittedAt) { - this.submittedAt = submittedAt; - return this; - } - - public Change build() { - return new Change(id, status, submittedAt); - } - - public Builder from(Change in) { - return this.id(in.id).status(in.status).submittedAt(in.submittedAt); - } - } private final String id; private final Status status; @@ -135,17 +92,18 @@ public final class Change { public boolean equals(Object obj) { if (this == obj) return true; - if (obj == null) + if (obj == null || getClass() != obj.getClass()) return false; - if (getClass() != obj.getClass()) - return false; - Change other = (Change) obj; - return Objects.equal(this.id, other.id); + Change other = Change.class.cast(obj); + return equal(this.id, other.id); } @Override public String toString() { - return Objects.toStringHelper(this).add("id", id).add("status", status).add("submittedAt", submittedAt) - .toString(); + return toStringHelper(this).add("id", id).add("status", status).add("submittedAt", submittedAt).toString(); + } + + public static Change create(String id, Status status, Date submittedAt) { + return new Change(id, status, submittedAt); } } diff --git a/labs/route53/src/main/java/org/jclouds/route53/domain/NewZone.java b/labs/route53/src/main/java/org/jclouds/route53/domain/NewZone.java new file mode 100644 index 0000000000..a4aa9fc0b3 --- /dev/null +++ b/labs/route53/src/main/java/org/jclouds/route53/domain/NewZone.java @@ -0,0 +1,87 @@ +/** + * 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 com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; + +/** + * + * @author Adrian Cole + */ +public final class NewZone { + + private final ZoneAndNameServers zone; + private final Change change; + + private NewZone(ZoneAndNameServers zone, Change change) { + this.zone = checkNotNull(zone, "zone"); + this.change = checkNotNull(change, "change of %s", zone); + } + + /** + * @see ZoneAndNameServers#getZone() + */ + public Zone getZone() { + return zone.getZone(); + } + + /** + * @see ZoneAndNameServers#getNameServers() + */ + public ImmutableList getNameServers() { + return zone.getNameServers(); + } + + /** + * the zone creation event + */ + public Change getChange() { + return change; + } + + @Override + public int hashCode() { + return Objects.hashCode(zone); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + NewZone that = NewZone.class.cast(obj); + return equal(this.zone, that.zone); + } + + @Override + public String toString() { + return toStringHelper("").add("zone", zone.getZone()).add("nameServers", zone.getNameServers()) + .add("change", change).toString(); + } + + public static NewZone create(ZoneAndNameServers zone, Change change) { + return new NewZone(zone, change); + } +} diff --git a/labs/route53/src/main/java/org/jclouds/route53/domain/Zone.java b/labs/route53/src/main/java/org/jclouds/route53/domain/Zone.java index 2633d56c6a..226e6ff0b5 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/domain/Zone.java +++ b/labs/route53/src/main/java/org/jclouds/route53/domain/Zone.java @@ -18,6 +18,8 @@ */ 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 com.google.common.base.Objects; @@ -28,6 +30,76 @@ import com.google.common.base.Optional; * @author Adrian Cole */ public final class Zone { + + private final String id; + private final String name; + private final String callerReference; + private final int resourceRecordSetCount; + private final Optional comment; + + private Zone(String id, String name, String callerReference, int resourceRecordSetCount, Optional comment) { + this.id = checkNotNull(id, "id"); + this.name = checkNotNull(name, "name"); + this.callerReference = checkNotNull(callerReference, "callerReference for %s", name); + this.resourceRecordSetCount = checkNotNull(resourceRecordSetCount, "resourceRecordSetCount for %s", name); + this.comment = checkNotNull(comment, "comment for %s", comment); + } + + /** + * The ID of the hosted zone. + */ + public String getId() { + return id; + } + + /** + * The name of the domain. + */ + public String getName() { + return name; + } + + /** + * A unique string that identifies the request to create the hosted zone. + */ + public String getCallerReference() { + return callerReference; + } + + /** + * A percentage value that indicates the size of the policy in packed form. + */ + public int getResourceRecordSetCount() { + return resourceRecordSetCount; + } + + public Optional getComment() { + return comment; + } + + @Override + public int hashCode() { + return Objects.hashCode(id, name, callerReference); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + Zone other = Zone.class.cast(obj); + return equal(this.id, other.id) && equal(this.name, other.name) + && equal(this.callerReference, other.callerReference); + } + + @Override + public String toString() { + return toStringHelper(this).omitNullValues().add("id", id).add("name", name) + .add("callerReference", callerReference).add("resourceRecordSetCount", resourceRecordSetCount) + .add("comment", comment.orNull()).toString(); + } + public static Builder builder() { return new Builder(); } @@ -92,76 +164,4 @@ public final class Zone { .resourceRecordSetCount(in.resourceRecordSetCount).comment(in.comment.orNull()); } } - - private final String id; - private final String name; - private final String callerReference; - private final int resourceRecordSetCount; - private final Optional comment; - - private Zone(String id, String name, String callerReference, int resourceRecordSetCount, - Optional comment) { - this.id = checkNotNull(id, "id"); - this.name = checkNotNull(name, "name"); - this.callerReference = checkNotNull(callerReference, "callerReference for %s", name); - this.resourceRecordSetCount = checkNotNull(resourceRecordSetCount, "resourceRecordSetCount for %s", name); - this.comment = checkNotNull(comment, "comment for %s", comment); - } - - /** - * The ID of the hosted zone. - */ - public String getId() { - return id; - } - - /** - * The name of the domain. - */ - public String getName() { - return name; - } - - /** - * A unique string that identifies the request to create the hosted zone. - */ - public String getCallerReference() { - return callerReference; - } - - /** - * A percentage value that indicates the size of the policy in packed form. - */ - public int getResourceRecordSetCount() { - return resourceRecordSetCount; - } - - public Optional getComment() { - return comment; - } - - @Override - public int hashCode() { - return Objects.hashCode(id, name, callerReference); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Zone other = (Zone) obj; - return Objects.equal(this.id, other.id) && Objects.equal(this.name, other.name) - && Objects.equal(this.callerReference, other.callerReference); - } - - @Override - public String toString() { - return Objects.toStringHelper(this).omitNullValues().add("id", id).add("name", name) - .add("callerReference", callerReference).add("resourceRecordSetCount", resourceRecordSetCount) - .add("comment", comment.orNull()).toString(); - } } diff --git a/labs/route53/src/main/java/org/jclouds/route53/domain/ZoneAndNameServers.java b/labs/route53/src/main/java/org/jclouds/route53/domain/ZoneAndNameServers.java index 3c5d8c257f..1dafc8ca45 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/domain/ZoneAndNameServers.java +++ b/labs/route53/src/main/java/org/jclouds/route53/domain/ZoneAndNameServers.java @@ -18,67 +18,23 @@ */ 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.Set; - import com.google.common.base.Objects; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList; /** * * @author Adrian Cole */ public final class ZoneAndNameServers { - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return builder().from(this); - } - - public final static class Builder { - private Zone zone; - private ImmutableSet.Builder nameServers = ImmutableSet. builder(); - - /** - * @see ZoneAndNameServers#getZone() - */ - public Builder zone(Zone zone) { - this.zone = zone; - return this; - } - - /** - * @see ZoneAndNameServers#getNameServers() - */ - public Builder nameServers(Iterable nameServers) { - this.nameServers = ImmutableSet. builder().addAll(checkNotNull(nameServers, "nameServers")); - return this; - } - - /** - * @see ZoneAndNameServers#getNameServers() - */ - public Builder addNameServer(String nameServer) { - this.nameServers.add(checkNotNull(nameServer, "nameServer")); - return this; - } - - public ZoneAndNameServers build() { - return new ZoneAndNameServers(zone, nameServers.build()); - } - - public Builder from(ZoneAndNameServers in) { - return this.zone(in.zone).nameServers(in.nameServers); - } - } private final Zone zone; - private final ImmutableSet nameServers; + private final ImmutableList nameServers; - private ZoneAndNameServers(Zone zone, ImmutableSet nameServers) { + private ZoneAndNameServers(Zone zone, ImmutableList nameServers) { this.zone = checkNotNull(zone, "zone"); this.nameServers = checkNotNull(nameServers, "nameServers for %s", zone); } @@ -93,7 +49,7 @@ public final class ZoneAndNameServers { /** * the authoritative name servers for the hosted zone */ - public Set getNameServers() { + public ImmutableList getNameServers() { return nameServers; } @@ -106,16 +62,18 @@ public final class ZoneAndNameServers { public boolean equals(Object obj) { if (this == obj) return true; - if (obj == null) + if (obj == null || getClass() != obj.getClass()) return false; - if (getClass() != obj.getClass()) - return false; - ZoneAndNameServers that = (ZoneAndNameServers) obj; - return Objects.equal(this.zone, that.zone) && Objects.equal(this.nameServers, that.nameServers); + ZoneAndNameServers that = ZoneAndNameServers.class.cast(obj); + return equal(this.zone, that.zone) && equal(this.nameServers, that.nameServers); } @Override public String toString() { - return Objects.toStringHelper("").add("zone", zone).add("nameServers", nameServers).toString(); + return toStringHelper("").add("zone", zone).add("nameServers", nameServers).toString(); + } + + public static ZoneAndNameServers create(Zone zone, Iterable nameServers) { + return new ZoneAndNameServers(zone, ImmutableList. copyOf(checkNotNull(nameServers, "nameServers"))); } } diff --git a/labs/route53/src/main/java/org/jclouds/route53/features/ZoneApi.java b/labs/route53/src/main/java/org/jclouds/route53/features/ZoneApi.java index d4ddb7283c..18aaf2aac2 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/features/ZoneApi.java +++ b/labs/route53/src/main/java/org/jclouds/route53/features/ZoneApi.java @@ -21,30 +21,48 @@ package org.jclouds.route53.features; import org.jclouds.collect.IterableWithMarker; import org.jclouds.collect.PagedIterable; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.route53.domain.Change; +import org.jclouds.route53.domain.Change.Status; +import org.jclouds.route53.domain.NewZone; import org.jclouds.route53.domain.Zone; import org.jclouds.route53.domain.ZoneAndNameServers; import org.jclouds.route53.options.ListZonesOptions; /** * @see ZoneAsyncApi - * @see + * @see * @author Adrian Cole */ public interface ZoneApi { /** - * Retrieves information about the specified zone, including its nameserver configuration + * This action creates a new hosted zone. + * + *

Note

+ * + * You cannot create a hosted zone for a top-level domain (TLD). * * @param name - * Name of the zone to get information about. - * @return null if not found + * The name of the domain. ex. {@code www.example.com.} The + * trailing dot is optional. + * @param callerReference + * A unique string that identifies the request and allows safe + * retries. ex. {@code MyDNSMigration_01} + * @return the new zone in progress, in {@link Status#PENDING}. */ - @Nullable - ZoneAndNameServers get(String name); + NewZone createWithReference(String name, String callerReference); /** - * Lists the zones that have the specified path prefix. If there are none, the action returns an - * empty list. + * like {@link #createWithReference(String, String)}, except you can specify + * a comment. + */ + 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. * *
* You can paginate the results using the {@link ListZonesOptions parameter} @@ -57,11 +75,32 @@ public interface ZoneApi { IterableWithMarker list(ListZonesOptions options); /** - * Lists the zones that have the specified path prefix. If there are none, the action returns an - * empty list. + * Lists the zones that have the specified path prefix. If there are none, + * the action returns an empty list. * * @return the response object */ PagedIterable list(); + /** + * Retrieves information about the specified zone, including its nameserver + * configuration + * + * @param id + * id of the zone to get information about. ex + * {@code Z1PA6795UKMFR9} + * @return null if not found + */ + @Nullable + ZoneAndNameServers get(String id); + + /** + * This action deletes a hosted zone. + * + * @param id + * id of the zone to delete. ex {@code Z1PA6795UKMFR9} + * @return null if not found or the change in progress + */ + @Nullable + Change delete(String id); } diff --git a/labs/route53/src/main/java/org/jclouds/route53/features/ZoneAsyncApi.java b/labs/route53/src/main/java/org/jclouds/route53/features/ZoneAsyncApi.java index b68824c2d8..446ec9f6eb 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/features/ZoneAsyncApi.java +++ b/labs/route53/src/main/java/org/jclouds/route53/features/ZoneAsyncApi.java @@ -18,24 +18,35 @@ */ package org.jclouds.route53.features; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; + import javax.inject.Named; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; import org.jclouds.Fallbacks.NullOnNotFoundOr404; import org.jclouds.collect.IterableWithMarker; import org.jclouds.collect.PagedIterable; import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; 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.domain.Change; +import org.jclouds.route53.domain.NewZone; import org.jclouds.route53.domain.Zone; import org.jclouds.route53.domain.ZoneAndNameServers; import org.jclouds.route53.filters.RestAuthentication; import org.jclouds.route53.functions.ZonesToPagedIterable; import org.jclouds.route53.options.ListZonesOptions; +import org.jclouds.route53.xml.ChangeHandler; +import org.jclouds.route53.xml.CreateHostedZoneResponseHandler; import org.jclouds.route53.xml.GetHostedZoneResponseHandler; import org.jclouds.route53.xml.ListHostedZonesResponseHandler; @@ -52,16 +63,29 @@ import com.google.common.util.concurrent.ListenableFuture; @VirtualHost @Path("/{jclouds.api-version}") public interface ZoneAsyncApi { + /** + * @see ZoneApi#createWithReference + */ + @Named("CreateHostedZone") + @POST + @Produces(APPLICATION_XML) + @Path("/hostedzone") + @Payload("{name}{callerReference}") + @XMLResponseParser(CreateHostedZoneResponseHandler.class) + ListenableFuture createWithReference(@PayloadParam("name") String name, + @PayloadParam("callerReference") String callerReference); /** - * @see ZoneApi#get() + * @see ZoneApi#createWithReferenceAndComment */ - @Named("GetHostedZone") - @GET - @Path("{zoneId}") - @XMLResponseParser(GetHostedZoneResponseHandler.class) - @Fallback(NullOnNotFoundOr404.class) - ListenableFuture get(@PathParam("zoneId") String zoneId); + @Named("CreateHostedZone") + @POST + @Produces(APPLICATION_XML) + @Path("/hostedzone") + @Payload("{name}{callerReference}{comment}") + @XMLResponseParser(CreateHostedZoneResponseHandler.class) + ListenableFuture createWithReferenceAndComment(@PayloadParam("name") String name, + @PayloadParam("callerReference") String callerReference, @PayloadParam("comment") String comment); /** * @see ZoneApi#list() @@ -82,4 +106,23 @@ public interface ZoneAsyncApi { @XMLResponseParser(ListHostedZonesResponseHandler.class) ListenableFuture> list(ListZonesOptions options); + /** + * @see ZoneApi#get() + */ + @Named("GetHostedZone") + @GET + @Path("/hostedzone/{zoneId}") + @XMLResponseParser(GetHostedZoneResponseHandler.class) + @Fallback(NullOnNotFoundOr404.class) + ListenableFuture get(@PathParam("zoneId") String zoneId); + + /** + * @see ZoneApi#delete() + */ + @Named("DeleteHostedZone") + @DELETE + @Path("/hostedzone/{zoneId}") + @XMLResponseParser(ChangeHandler.class) + @Fallback(NullOnNotFoundOr404.class) + ListenableFuture delete(@PathParam("zoneId") String zoneId); } diff --git a/labs/route53/src/main/java/org/jclouds/route53/xml/ChangeHandler.java b/labs/route53/src/main/java/org/jclouds/route53/xml/ChangeHandler.java index 8c0d4ce947..2e0c2fa269 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/xml/ChangeHandler.java +++ b/labs/route53/src/main/java/org/jclouds/route53/xml/ChangeHandler.java @@ -18,13 +18,17 @@ */ package org.jclouds.route53.xml; +import static org.jclouds.util.SaxUtils.currentOrNull; + +import java.util.Date; + import javax.inject.Inject; import org.jclouds.date.DateService; import org.jclouds.http.functions.ParseSax; import org.jclouds.route53.domain.Change; import org.jclouds.route53.domain.Change.Status; -import org.jclouds.util.SaxUtils; +import org.xml.sax.Attributes; /** * @see
+ * + * @author Adrian Cole + */ +public class CreateHostedZoneResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult { + + private final GetHostedZoneResponseHandler zoneHandler; + private final ChangeHandler changeHandler; + + private boolean inChange; + + @Inject + public CreateHostedZoneResponseHandler(GetHostedZoneResponseHandler zoneHandler, ChangeHandler changeHandler) { + this.zoneHandler = zoneHandler; + this.changeHandler = changeHandler; + } + + @Override + public NewZone getResult() { + ZoneAndNameServers zone = zoneHandler.getResult(); + return NewZone.create(zone, changeHandler.getResult()); + } + + @Override + public void startElement(String url, String name, String qName, Attributes attributes) { + if (equalsOrSuffix(qName, "ChangeInfo")) { + inChange = true; + } + if (inChange) { + changeHandler.startElement(url, name, qName, attributes); + } else { + zoneHandler.startElement(url, name, qName, attributes); + } + } + + @Override + public void endElement(String uri, String name, String qName) { + if (inChange) { + if (qName.equals("ChangeInfo")) { + inChange = false; + } else { + changeHandler.endElement(uri, name, qName); + } + } else { + zoneHandler.endElement(uri, name, qName); + } + } + + @Override + public void characters(char ch[], int start, int length) { + if (inChange) { + changeHandler.characters(ch, start, length); + } else { + zoneHandler.characters(ch, start, length); + } + } +} diff --git a/labs/route53/src/main/java/org/jclouds/route53/xml/GetHostedZoneResponseHandler.java b/labs/route53/src/main/java/org/jclouds/route53/xml/GetHostedZoneResponseHandler.java index 3e2b179bb8..1502e9bfe0 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/xml/GetHostedZoneResponseHandler.java +++ b/labs/route53/src/main/java/org/jclouds/route53/xml/GetHostedZoneResponseHandler.java @@ -18,12 +18,16 @@ */ package org.jclouds.route53.xml; -import org.jclouds.http.functions.ParseSax; -import org.jclouds.route53.domain.ZoneAndNameServers; -import org.jclouds.util.SaxUtils; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; +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.Zone; +import org.jclouds.route53.domain.ZoneAndNameServers; +import org.xml.sax.Attributes; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; import com.google.inject.Inject; /** @@ -38,9 +42,12 @@ public class GetHostedZoneResponseHandler extends ParseSax.HandlerForGeneratedRe private final ZoneHandler zoneHandler; private StringBuilder currentText = new StringBuilder(); - private ZoneAndNameServers.Builder builder = ZoneAndNameServers.builder(); + private boolean inZone; + private Zone zone; + private Builder nameServers = ImmutableList. builder(); + @Inject public GetHostedZoneResponseHandler(ZoneHandler zoneHandler) { this.zoneHandler = zoneHandler; @@ -49,15 +56,16 @@ public class GetHostedZoneResponseHandler extends ParseSax.HandlerForGeneratedRe @Override public ZoneAndNameServers getResult() { try { - return builder.build(); + return ZoneAndNameServers.create(zone, nameServers.build()); } finally { - builder = ZoneAndNameServers.builder(); + zone = null; + nameServers = ImmutableList. builder(); } } @Override - public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException { - if (SaxUtils.equalsOrSuffix(qName, "HostedZone")) { + public void startElement(String url, String name, String qName, Attributes attributes) { + if (equalsOrSuffix(qName, "HostedZone")) { inZone = true; } if (inZone) { @@ -66,16 +74,16 @@ public class GetHostedZoneResponseHandler extends ParseSax.HandlerForGeneratedRe } @Override - public void endElement(String uri, String name, String qName) throws SAXException { + public void endElement(String uri, String name, String qName) { if (inZone) { if (qName.equals("HostedZone")) { inZone = false; - builder.zone(zoneHandler.getResult()); + zone = zoneHandler.getResult(); } else { zoneHandler.endElement(uri, name, qName); } } else if (qName.equals("NameServer")) { - builder.addNameServer(SaxUtils.currentOrNull(currentText)); + nameServers.add(currentOrNull(currentText)); } currentText = new StringBuilder(); diff --git a/labs/route53/src/main/java/org/jclouds/route53/xml/ListHostedZonesResponseHandler.java b/labs/route53/src/main/java/org/jclouds/route53/xml/ListHostedZonesResponseHandler.java index 600fbe1fc0..94e21e99ba 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/xml/ListHostedZonesResponseHandler.java +++ b/labs/route53/src/main/java/org/jclouds/route53/xml/ListHostedZonesResponseHandler.java @@ -18,13 +18,14 @@ */ package org.jclouds.route53.xml; +import static org.jclouds.util.SaxUtils.currentOrNull; +import static org.jclouds.util.SaxUtils.equalsOrSuffix; + import org.jclouds.collect.IterableWithMarker; import org.jclouds.collect.IterableWithMarkers; import org.jclouds.http.functions.ParseSax; import org.jclouds.route53.domain.Zone; -import org.jclouds.util.SaxUtils; import org.xml.sax.Attributes; -import org.xml.sax.SAXException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; @@ -52,9 +53,6 @@ public class ListHostedZonesResponseHandler extends this.zoneHandler = zoneHandler; } - /** - * {@inheritDoc} - */ @Override public IterableWithMarker getResult() { try { @@ -64,12 +62,9 @@ public class ListHostedZonesResponseHandler extends } } - /** - * {@inheritDoc} - */ @Override - public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException { - if (SaxUtils.equalsOrSuffix(qName, "HostedZones")) { + public void startElement(String url, String name, String qName, Attributes attributes) { + if (equalsOrSuffix(qName, "HostedZones")) { inZones = true; } if (inZones) { @@ -77,11 +72,8 @@ public class ListHostedZonesResponseHandler extends } } - /** - * {@inheritDoc} - */ @Override - public void endElement(String uri, String name, String qName) throws SAXException { + public void endElement(String uri, String name, String qName) { if (inZones) { if (qName.equals("HostedZones")) { inZones = false; @@ -91,15 +83,12 @@ public class ListHostedZonesResponseHandler extends zoneHandler.endElement(uri, name, qName); } } else if (qName.equals("NextMarker")) { - afterMarker = SaxUtils.currentOrNull(currentText); + afterMarker = currentOrNull(currentText); } currentText = new StringBuilder(); } - /** - * {@inheritDoc} - */ @Override public void characters(char ch[], int start, int length) { if (inZones) { @@ -108,5 +97,4 @@ public class ListHostedZonesResponseHandler extends currentText.append(ch, start, length); } } - } diff --git a/labs/route53/src/main/java/org/jclouds/route53/xml/ZoneHandler.java b/labs/route53/src/main/java/org/jclouds/route53/xml/ZoneHandler.java index 99c60b149f..7b785eb538 100644 --- a/labs/route53/src/main/java/org/jclouds/route53/xml/ZoneHandler.java +++ b/labs/route53/src/main/java/org/jclouds/route53/xml/ZoneHandler.java @@ -22,6 +22,7 @@ import static org.jclouds.util.SaxUtils.currentOrNull; import org.jclouds.http.functions.ParseSax; import org.jclouds.route53.domain.Zone; +import org.xml.sax.Attributes; /** * @@ -32,9 +33,6 @@ public class ZoneHandler extends ParseSax.HandlerForGeneratedRequestWithResultjclouds.org.expect", + "application/xml")).build(); + + HttpResponse createResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType("/new_zone.xml", "text/xml")).build(); + + public void testCreateWithReferenceWhenResponseIs2xx() { + Route53Api success = requestSendsResponse(createWithReference, createResponse); + assertEquals(success.getZoneApi().createWithReference("jclouds.org.", "expect").toString(), + new CreateHostedZoneResponseTest().expected().toString()); + } + + HttpRequest createWithReferenceAndComment = HttpRequest.builder().method("POST") + .endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone") + .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=") + .payload( + payloadFromStringWithContentType( + "jclouds.org.expectcomment", + "application/xml")).build(); + + public void testCreateWithReferenceAndCommentWhenResponseIs2xx() { + Route53Api success = requestSendsResponse(createWithReferenceAndComment, createResponse); + assertEquals(success.getZoneApi().createWithReferenceAndComment("jclouds.org.", "expect", "comment").toString(), + new CreateHostedZoneResponseTest().expected().toString()); + } HttpRequest get = HttpRequest.builder().method("GET") - .endpoint("https://route53.amazonaws.com/2012-02-29//hostedzone/Z1XTHCPEFRWV1X") + .endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1XTHCPEFRWV1X") .addHeader("Host", "route53.amazonaws.com") .addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800") .addHeader("X-Amzn-Authorization", @@ -51,16 +89,16 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest { .payload(payloadFromResourceWithContentType("/hosted_zone.xml", "text/xml")).build(); public void testGetWhenResponseIs2xx() { - Route53Api apiWhenExist = requestSendsResponse(get, getResponse); - assertEquals(apiWhenExist.getZoneApi().get("/hostedzone/Z1XTHCPEFRWV1X").toString(), new GetHostedZoneResponseTest().expected() + Route53Api success = requestSendsResponse(get, getResponse); + assertEquals(success.getZoneApi().get("Z1XTHCPEFRWV1X").toString(), new GetHostedZoneResponseTest().expected() .toString()); } HttpResponse notFound = HttpResponse.builder().statusCode(404).build(); public void testGetWhenResponseIs404() { - Route53Api apiWhenDontExist = requestSendsResponse(get, notFound); - assertNull(apiWhenDontExist.getZoneApi().get("/hostedzone/Z1XTHCPEFRWV1X")); + Route53Api fail = requestSendsResponse(get, notFound); + assertNull(fail.getZoneApi().get("Z1XTHCPEFRWV1X")); } HttpRequest list = HttpRequest.builder().method("GET") @@ -75,16 +113,16 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest { .payload(payloadFromResourceWithContentType("/hosted_zones.xml", "text/xml")).build(); public void testListWhenResponseIs2xx() { - Route53Api apiWhenExist = requestSendsResponse(list, listResponse); - assertEquals(apiWhenExist.getZoneApi().list().get(0).toString(), new ListHostedZonesResponseTest().expected() + Route53Api success = requestSendsResponse(list, listResponse); + assertEquals(success.getZoneApi().list().get(0).toString(), new ListHostedZonesResponseTest().expected() .toString()); } // TODO: this should really be an empty set @Test(expectedExceptions = ResourceNotFoundException.class) public void testListWhenResponseIs404() { - Route53Api apiWhenDontExist = requestSendsResponse(list, notFound); - assertEquals(apiWhenDontExist.getZoneApi().list().get(0).toSet(), ImmutableSet.of()); + Route53Api fail = requestSendsResponse(list, notFound); + assertEquals(fail.getZoneApi().list().get(0).toSet(), ImmutableSet.of()); } HttpRequest listWithOptions = HttpRequest.builder().method("GET") @@ -100,14 +138,34 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest { assertEquals(apiWhenWithOptionsExist.getZoneApi().list(afterMarker("Z333333YYYYYYY")).toString(), new ListHostedZonesResponseTest().expected().toString()); } - public void testList2PagesWhenResponseIs2xx() { HttpResponse noMore = HttpResponse.builder().statusCode(200) .payload(payloadFromStringWithContentType("", "text/xml")).build(); - Route53Api apiWhenExist = requestsSendResponses(list, listResponse, listWithOptions, noMore); - assertEquals(apiWhenExist.getZoneApi().list().concat().toString(), new ListHostedZonesResponseTest().expected() + Route53Api success = requestsSendResponses(list, listResponse, listWithOptions, noMore); + assertEquals(success.getZoneApi().list().concat().toString(), new ListHostedZonesResponseTest().expected() .toString()); } + + HttpRequest delete = HttpRequest.builder().method("DELETE") + .endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1XTHCPEFRWV1X") + .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 deleteResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResourceWithContentType("/change.xml", "text/xml")).build(); + + public void testDeleteWhenResponseIs2xx() { + Route53Api success = requestSendsResponse(delete, deleteResponse); + assertEquals(success.getZoneApi().delete("Z1XTHCPEFRWV1X").toString(), new GetChangeResponseTest().expected().toString()); + } + + public void testDeleteWhenResponseIs404() { + Route53Api fail = requestSendsResponse(delete, notFound); + assertNull(fail.getZoneApi().delete("Z1XTHCPEFRWV1X")); + } } diff --git a/labs/route53/src/test/java/org/jclouds/route53/features/ZoneApiLiveTest.java b/labs/route53/src/test/java/org/jclouds/route53/features/ZoneApiLiveTest.java index b2725d9e00..d6dfee2fc4 100644 --- a/labs/route53/src/test/java/org/jclouds/route53/features/ZoneApiLiveTest.java +++ b/labs/route53/src/test/java/org/jclouds/route53/features/ZoneApiLiveTest.java @@ -19,14 +19,28 @@ package org.jclouds.route53.features; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.jclouds.route53.domain.Change.Status.INSYNC; +import static org.jclouds.route53.domain.Change.Status.PENDING; +import static org.jclouds.util.Predicates2.retry; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import java.util.Date; +import java.util.logging.Logger; + +import org.jclouds.JcloudsVersion; import org.jclouds.collect.IterableWithMarker; +import org.jclouds.route53.domain.Change; +import org.jclouds.route53.domain.NewZone; import org.jclouds.route53.domain.Zone; import org.jclouds.route53.internal.BaseRoute53ApiLiveTest; import org.jclouds.route53.options.ListZonesOptions; -import org.testng.Assert; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import com.google.common.base.Predicate; import com.google.common.collect.Iterables; /** @@ -34,25 +48,37 @@ import com.google.common.collect.Iterables; */ @Test(groups = "live", testName = "ZoneApiLiveTest") public class ZoneApiLiveTest extends BaseRoute53ApiLiveTest { + private Predicate inSync; + + @BeforeClass(groups = "live") + @Override + public void setupContext() { + super.setupContext(); + inSync = retry(new Predicate() { + public boolean apply(Change input) { + return context.getApi().getChange(input.getId()).getStatus() == INSYNC; + } + }, 600, 1, 5, SECONDS); + } private void checkZone(Zone zone) { - checkNotNull(zone.getId(), "Id cannot be null for a Zone."); - checkNotNull(zone.getName(), "Id cannot be null for a Zone."); - checkNotNull(zone.getCallerReference(), "CallerReference cannot be null for a Zone."); - checkNotNull(zone.getComment(), "While Comment can be null for a Zone, its Optional wrapper cannot."); + 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.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); } @Test protected void testListZones() { IterableWithMarker response = api().list().get(0); - + for (Zone zone : response) { checkZone(zone); } - + if (Iterables.size(response) > 0) { Zone zone = response.iterator().next(); - Assert.assertEquals(api().get(zone.getId()).getZone(), zone); + assertEquals(api().get(zone.getId()).getZone(), zone); } // Test with a Marker, even if it's null @@ -62,6 +88,38 @@ public class ZoneApiLiveTest extends BaseRoute53ApiLiveTest { } } + @Test + public void testGetZoneWhenNotFound() { + assertNull(api().get("AAAAAAAAAAAAAAAA")); + } + + @Test + public void testDeleteZoneWhenNotFound() { + assertNull(api().delete("AAAAAAAAAAAAAAAA")); + } + + @Test + public void testCreateAndDeleteZone() { + String name = System.getProperty("user.name").replace('.', '-') + ".zone.route53test.jclouds.org."; + String nonce = name + " @ " + new Date(); + String comment = name + " for " + JcloudsVersion.get(); + NewZone newZone = api().createWithReferenceAndComment(name, nonce, comment); + Logger.getAnonymousLogger().info("created zone: " + newZone); + try { + checkZone(newZone.getZone()); + assertEquals(newZone.getChange().getStatus(), PENDING, "invalid status on zone " + newZone); + assertTrue(newZone.getNameServers().size() > 0, "no name servers for zone " + newZone); + assertEquals(newZone.getZone().getName(), name); + assertEquals(newZone.getZone().getCallerReference(), nonce); + assertEquals(newZone.getZone().getComment().get(), comment); + + assertTrue(inSync.apply(newZone.getChange()), "zone didn't sync " + newZone); + } finally { + Change delete = api().delete(newZone.getZone().getId()); + assertTrue(inSync.apply(delete), "delete didn't sync " + delete); + } + } + protected ZoneApi api() { return context.getApi().getZoneApi(); } diff --git a/labs/route53/src/test/java/org/jclouds/route53/internal/BaseRoute53AsyncApiExpectTest.java b/labs/route53/src/test/java/org/jclouds/route53/internal/BaseRoute53AsyncApiExpectTest.java deleted file mode 100644 index befbf8bca4..0000000000 --- a/labs/route53/src/test/java/org/jclouds/route53/internal/BaseRoute53AsyncApiExpectTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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.internal; - -import java.util.Properties; - -import org.jclouds.http.HttpRequest; -import org.jclouds.http.HttpResponse; -import org.jclouds.route53.Route53AsyncApi; - -import com.google.common.base.Function; -import com.google.inject.Module; - -/** - * - * @author Adrian Cole - */ -public class BaseRoute53AsyncApiExpectTest extends BaseRoute53ExpectTest { - public Route53AsyncApi createApi(Function fn, Module module, Properties props) { - return createInjector(fn, module, props).getInstance(Route53AsyncApi.class); - } -} diff --git a/labs/route53/src/test/java/org/jclouds/route53/parse/CreateHostedZoneResponseTest.java b/labs/route53/src/test/java/org/jclouds/route53/parse/CreateHostedZoneResponseTest.java new file mode 100644 index 0000000000..3b40f18f72 --- /dev/null +++ b/labs/route53/src/test/java/org/jclouds/route53/parse/CreateHostedZoneResponseTest.java @@ -0,0 +1,51 @@ +/** + * 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.NewZone; +import org.jclouds.route53.xml.CreateHostedZoneResponseHandler; +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 = "CreateHostedZoneResponseTest") +public class CreateHostedZoneResponseTest extends BaseHandlerTest { + + public void test() { + InputStream is = getClass().getResourceAsStream("/new_zone.xml"); + + NewZone expected = expected(); + + CreateHostedZoneResponseHandler handler = injector.getInstance(CreateHostedZoneResponseHandler.class); + NewZone result = factory.create(handler).parse(is); + + assertEquals(result, expected); + } + + public NewZone expected() { + return NewZone.create(new GetHostedZoneResponseTest().expected(), new GetChangeResponseTest().expected()); + } +} diff --git a/labs/route53/src/test/java/org/jclouds/route53/parse/GetChangeResponseTest.java b/labs/route53/src/test/java/org/jclouds/route53/parse/GetChangeResponseTest.java index d8e431cb43..e6a77d5bca 100644 --- a/labs/route53/src/test/java/org/jclouds/route53/parse/GetChangeResponseTest.java +++ b/labs/route53/src/test/java/org/jclouds/route53/parse/GetChangeResponseTest.java @@ -21,6 +21,7 @@ package org.jclouds.route53.parse; import static org.testng.Assert.assertEquals; import java.io.InputStream; +import java.util.Date; import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.http.functions.BaseHandlerTest; @@ -50,11 +51,8 @@ public class GetChangeResponseTest extends BaseHandlerTest { } public Change expected() { - return Change.builder() - .id("C2682N5HXP0BZ4") - .status(Status.INSYNC) - .submittedAt(new SimpleDateFormatDateService().iso8601DateParse("2011-09-10T01:36:41.958Z")) - .build(); + Date submittedAt = new SimpleDateFormatDateService().iso8601DateParse("2011-09-10T01:36:41.958Z"); + return Change.create("C2682N5HXP0BZ4", Status.INSYNC, submittedAt); } } diff --git a/labs/route53/src/test/java/org/jclouds/route53/parse/GetHostedZoneResponseTest.java b/labs/route53/src/test/java/org/jclouds/route53/parse/GetHostedZoneResponseTest.java index c485b1d6a3..135744fe9f 100644 --- a/labs/route53/src/test/java/org/jclouds/route53/parse/GetHostedZoneResponseTest.java +++ b/labs/route53/src/test/java/org/jclouds/route53/parse/GetHostedZoneResponseTest.java @@ -28,6 +28,8 @@ import org.jclouds.route53.domain.ZoneAndNameServers; import org.jclouds.route53.xml.GetHostedZoneResponseHandler; import org.testng.annotations.Test; +import com.google.common.collect.ImmutableList; + /** * @author Adrian Cole */ @@ -47,15 +49,15 @@ public class GetHostedZoneResponseTest extends BaseHandlerTest { } public ZoneAndNameServers expected() { - return ZoneAndNameServers.builder() - .addNameServer("ns-1638.awsdns-12.co.uk") - .addNameServer("ns-144.awsdns-18.com") - .addNameServer("ns-781.awsdns-33.net") - .addNameServer("ns-1478.awsdns-56.org") - .zone(Zone.builder() - .id("/hostedzone/Z21DW1QVGID6NG") - .name("example.com.") - .callerReference("a_unique_reference") - .comment("Migrate an existing domain to Route 53").build()).build(); + return ZoneAndNameServers.create(Zone.builder() + .id("Z21DW1QVGID6NG") + .name("example.com.") + .callerReference("a_unique_reference") + .comment("Migrate an existing domain to Route 53").build(), + ImmutableList. builder() + .add("ns-1638.awsdns-12.co.uk") + .add("ns-144.awsdns-18.com") + .add("ns-781.awsdns-33.net") + .add("ns-1478.awsdns-56.org").build()); } } diff --git a/labs/route53/src/test/java/org/jclouds/route53/parse/ListHostedZonesResponseTest.java b/labs/route53/src/test/java/org/jclouds/route53/parse/ListHostedZonesResponseTest.java index 8e7bcc4bc8..ac624b5178 100644 --- a/labs/route53/src/test/java/org/jclouds/route53/parse/ListHostedZonesResponseTest.java +++ b/labs/route53/src/test/java/org/jclouds/route53/parse/ListHostedZonesResponseTest.java @@ -54,13 +54,13 @@ public class ListHostedZonesResponseTest extends BaseHandlerTest { return IterableWithMarkers.from( ImmutableSet.of( Zone.builder() - .id("/hostedzone/Z21DW1QVGID6NG") + .id("Z21DW1QVGID6NG") .name("example.com.") .callerReference("a_unique_reference") .resourceRecordSetCount(17) .comment("Migrate an existing domain to Route 53").build(), Zone.builder() - .id("/hostedzone/Z2682N5HXP0BZ4") + .id("Z2682N5HXP0BZ4") .name("example2.com.") .callerReference("a_unique_reference2") .resourceRecordSetCount(117) diff --git a/labs/route53/src/test/resources/change.xml b/labs/route53/src/test/resources/change.xml index a8be9b25ac..9f38823be5 100644 --- a/labs/route53/src/test/resources/change.xml +++ b/labs/route53/src/test/resources/change.xml @@ -1,7 +1,7 @@ - C2682N5HXP0BZ4 + /change/C2682N5HXP0BZ4 INSYNC 2011-09-10T01:36:41.958Z diff --git a/labs/route53/src/test/resources/new_zone.xml b/labs/route53/src/test/resources/new_zone.xml new file mode 100644 index 0000000000..be9fd9d83b --- /dev/null +++ b/labs/route53/src/test/resources/new_zone.xml @@ -0,0 +1,25 @@ + + + + /hostedzone/Z21DW1QVGID6NG + example.com. + a_unique_reference + + Migrate an existing domain to Route 53 + + + + C2682N5HXP0BZ4 + INSYNC + 2011-09-10T01:36:41.958Z + + + + ns-1638.awsdns-12.co.uk + ns-144.awsdns-18.com + ns-781.awsdns-33.net + ns-1478.awsdns-56.org + + + \ No newline at end of file