You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ad...@apache.org on 2014/11/10 18:19:41 UTC

[5/6] jclouds-labs-google git commit: * Removed the need for users to manually specify the current project name everywhere. * Documented why we implicitly lookup project name using project id; corrected README, pom, ApiMetadata and added tests. * I

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/UserProject.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/UserProject.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/UserProject.java
deleted file mode 100644
index e932d1c..0000000
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/config/UserProject.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.googlecomputeengine.config;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import javax.inject.Qualifier;
-
-/**
- * Qualifies a property as the user's project id.
- */
-@Retention(value = RetentionPolicy.RUNTIME)
-@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
-@Qualifier
-public @interface UserProject {
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java
index 73d3c4b..c7ccf75 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java
@@ -17,7 +17,6 @@
 package org.jclouds.googlecomputeengine.domain;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.jclouds.googlecomputeengine.domain.Instance.AttachedDisk.Type.PERSISTENT;
 
 import java.net.URI;
@@ -114,10 +113,10 @@ public abstract class NewInstance {
       }
    }
 
-   public abstract URI machineType();
-
    public abstract String name();
 
+   public abstract URI machineType();
+
    public abstract List<NetworkInterface> networkInterfaces();
 
    public abstract List<Disk> disks();
@@ -129,11 +128,12 @@ public abstract class NewInstance {
    /** Add metadata via {@link Metadata#items()}. */
    public abstract Metadata metadata();
 
-   public static NewInstance create(URI machineType, String name, URI network, Disk bootDisk, String description) {
-      return create(machineType, name, network, Arrays.asList(checkNotNull(bootDisk, "bootDisk")), description);
+   /** Convenience for creating a new instance with only a boot disk and minimal parameters. */
+   public static NewInstance create(String name, URI machineType, URI network, URI sourceImage) {
+      return create(name, machineType, network, Arrays.asList(Disk.newBootDisk(sourceImage)), null);
    }
 
-   public static NewInstance create(URI machineType, String name, URI network, List<Disk> disks, String description) {
+   public static NewInstance create(String name, URI machineType, URI network, List<Disk> disks, String description) {
       checkArgument(disks.get(0).boot(), "disk 0 must be a boot disk! %s", disks);
       boolean foundBoot = false;
       for (Disk disk : disks) {
@@ -142,14 +142,14 @@ public abstract class NewInstance {
             foundBoot = true;
          }
       }
-      return create(machineType, name, ImmutableList.of(NetworkInterface.create(network)), ImmutableList.copyOf(disks),
+      return create(name, machineType, ImmutableList.of(NetworkInterface.create(network)), ImmutableList.copyOf(disks),
             description, Tags.create(), Metadata.create());
    }
 
-   @SerializedNames({ "machineType", "name", "networkInterfaces", "disks", "description", "tags", "metadata" })
-   static NewInstance create(URI machineType, String name, List<NetworkInterface> networkInterfaces, List<Disk> disks,
-         String description, Tags tags, Metadata metadata) {
-      return new AutoValue_NewInstance(machineType, name, networkInterfaces, disks, description, tags, metadata);
+   @SerializedNames({ "name", "machineType", "networkInterfaces", "disks", "description", "tags", "metadata" })
+   static NewInstance create(String name, URI machineType, List<NetworkInterface> networkInterfaces,
+         List<Disk> disks, String description, Tags tags, Metadata metadata) {
+      return new AutoValue_NewInstance(name, machineType, networkInterfaces, disks, description, tags, metadata);
    }
 
    NewInstance() {

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AddressApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AddressApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AddressApi.java
index 679811d..1beef9f 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AddressApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AddressApi.java
@@ -17,11 +17,12 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -33,12 +34,11 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.domain.Address;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseAddresses;
+import org.jclouds.googlecomputeengine.internal.BaseCallerArg0ToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.oauth.v2.config.OAuthScopes;
@@ -47,11 +47,12 @@ import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/addresses")
@@ -69,7 +70,7 @@ public interface AddressApi {
 
    /**
     * Creates an address resource in the specified project specifying the size of the address.
-    * 
+    *
     * @param address the name of address.
     * @return an Operation resource. To check on the status of an operation, poll the Operations resource returned to
     *         you, and look for the status field.
@@ -95,36 +96,44 @@ public interface AddressApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("Addresses:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseAddresses.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<Address> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<Address> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Addresses:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseAddresses.class)
-   @Transform(ParseAddresses.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(AddressPages.class)
    Iterator<ListPage<Address>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Addresses:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseAddresses.class)
-   @Transform(ParseAddresses.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(AddressPages.class)
    Iterator<ListPage<Address>> list(ListOptions options);
+
+   static final class AddressPages extends BaseCallerArg0ToIteratorOfListPage<Address, AddressPages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject AddressPages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<Address>> fetchNextPage(final String regionName, final ListOptions options) {
+         return new Function<String, ListPage<Address>>() {
+            @Override public ListPage<Address> apply(String pageToken) {
+               return api.addressesInRegion(regionName).listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java
index 37a778a..3386231 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java
@@ -17,7 +17,7 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
 
 import java.util.Iterator;
 
@@ -32,25 +32,22 @@ import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.domain.Instance;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.MachineType;
-import org.jclouds.googlecomputeengine.functions.internal.BaseToIteratorOfListPage;
+import org.jclouds.googlecomputeengine.internal.BaseToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ListOptions;
-import org.jclouds.http.functions.ParseJson;
 import org.jclouds.javax.annotation.Nullable;
-import org.jclouds.json.Json;
 import org.jclouds.oauth.v2.config.OAuthScopes;
 import org.jclouds.oauth.v2.filters.OAuthAuthenticationFilter;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 
 import com.google.common.base.Function;
-import com.google.inject.TypeLiteral;
 
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/aggregated")
 @Consumes(APPLICATION_JSON)
+@OAuthScopes(COMPUTE_READONLY_SCOPE)
 public interface AggregatedListApi {
 
    /**
@@ -58,45 +55,29 @@ public interface AggregatedListApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("MachineTypes:aggregatedList")
    @GET
    @Path("/machineTypes")
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   ListPage<MachineType> pageOfMachineTypes(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<MachineType> pageOfMachineTypes(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #pageOfMachineTypes(String, ListOptions)
-    */
+   /** @see #pageOfMachineTypes(String, ListOptions) */
    @Named("MachineTypes:aggregatedList")
    @GET
    @Path("/machineTypes")
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(MachineTypePage.class)
    @Transform(MachineTypePages.class)
    Iterator<ListPage<MachineType>> machineTypes();
 
-   /**
-    * @see #pageOfMachineTypes(String, ListOptions)
-    */
+   /** @see #pageOfMachineTypes(String, ListOptions) */
    @Named("MachineTypes:aggregatedList")
    @GET
    @Path("/machineTypes")
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(MachineTypePage.class)
    @Transform(MachineTypePages.class)
    Iterator<ListPage<MachineType>> machineTypes(ListOptions options);
 
-   static final class MachineTypePage extends ParseJson<ListPage<MachineType>> {
-      @Inject MachineTypePage(Json json) {
-         super(json, new TypeLiteral<ListPage<MachineType>>() {
-         });
-      }
-   }
-
    static final class MachineTypePages extends BaseToIteratorOfListPage<MachineType, MachineTypePages> {
       private final GoogleComputeEngineApi api;
 
@@ -104,11 +85,10 @@ public interface AggregatedListApi {
          this.api = api;
       }
 
-      @Override
-      protected Function<String, ListPage<MachineType>> fetchNextPage(final String project, final ListOptions options) {
+      @Override protected Function<String, ListPage<MachineType>> fetchNextPage(final ListOptions options) {
          return new Function<String, ListPage<MachineType>>() {
-            @Override public ListPage<MachineType> apply(String input) {
-               return api.aggregatedList(project).pageOfMachineTypes(input, options);
+            @Override public ListPage<MachineType> apply(String pageToken) {
+               return api.aggregatedList().pageOfMachineTypes(pageToken, options);
             }
          };
       }
@@ -119,45 +99,29 @@ public interface AggregatedListApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("Instances:aggregatedList")
    @GET
    @Path("/instances")
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   ListPage<Instance> pageOfInstances(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<Instance> pageOfInstances(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #pageOfInstances(String, ListOptions)
-    */
+   /** @see #pageOfInstances(String, ListOptions) */
    @Named("Instances:aggregatedList")
    @GET
    @Path("/instances")
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(InstancePage.class)
    @Transform(InstancePages.class)
    Iterator<ListPage<Instance>> instances();
 
-   /**
-    * @see #pageOfInstances(String, ListOptions)
-    */
+   /** @see #pageOfInstances(String, ListOptions) */
    @Named("Instances:aggregatedList")
    @GET
    @Path("/instances")
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(InstancePage.class)
    @Transform(InstancePages.class)
    Iterator<ListPage<Instance>> instances(ListOptions options);
 
-   static final class InstancePage extends ParseJson<ListPage<Instance>> {
-      @Inject InstancePage(Json json) {
-         super(json, new TypeLiteral<ListPage<Instance>>() {
-         });
-      }
-   }
-
    static final class InstancePages extends BaseToIteratorOfListPage<Instance, InstancePages> {
       private final GoogleComputeEngineApi api;
 
@@ -165,11 +129,10 @@ public interface AggregatedListApi {
          this.api = api;
       }
 
-      @Override
-      protected Function<String, ListPage<Instance>> fetchNextPage(final String project, final ListOptions options) {
+      @Override protected Function<String, ListPage<Instance>> fetchNextPage(final ListOptions options) {
          return new Function<String, ListPage<Instance>>() {
-            @Override public ListPage<Instance> apply(String input) {
-               return api.aggregatedList(project).pageOfInstances(input, options);
+            @Override public ListPage<Instance> apply(String pageToken) {
+               return api.aggregatedList().pageOfInstances(pageToken, options);
             }
          };
       }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskApi.java
index 8b485de..9934d22 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskApi.java
@@ -17,11 +17,12 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -33,13 +34,12 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.binders.DiskCreationBinder;
 import org.jclouds.googlecomputeengine.domain.Disk;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseDisks;
+import org.jclouds.googlecomputeengine.internal.BaseCallerArg0ToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.DiskCreationOptions;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
@@ -49,11 +49,12 @@ import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/disks")
@@ -132,36 +133,44 @@ public interface DiskApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("Disks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseDisks.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<Disk> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<Disk> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Disks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseDisks.class)
-   @Transform(ParseDisks.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(DiskPages.class)
    Iterator<ListPage<Disk>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Disks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseDisks.class)
-   @Transform(ParseDisks.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(DiskPages.class)
    Iterator<ListPage<Disk>> list(ListOptions options);
+
+   static final class DiskPages extends BaseCallerArg0ToIteratorOfListPage<Disk, DiskPages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject DiskPages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<Disk>> fetchNextPage(final String zoneName, final ListOptions options) {
+         return new Function<String, ListPage<Disk>>() {
+            @Override public ListPage<Disk> apply(String pageToken) {
+               return api.disksInZone(zoneName).listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskTypeApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskTypeApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskTypeApi.java
index b16939f..b688ae4 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskTypeApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/DiskTypeApi.java
@@ -17,10 +17,11 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
 
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -29,32 +30,32 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.domain.DiskType;
 import org.jclouds.googlecomputeengine.domain.ListPage;
-import org.jclouds.googlecomputeengine.functions.internal.ParseDiskTypes;
+import org.jclouds.googlecomputeengine.internal.BaseCallerArg0ToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.oauth.v2.config.OAuthScopes;
 import org.jclouds.oauth.v2.filters.OAuthAuthenticationFilter;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/diskTypes")
 @Consumes(APPLICATION_JSON)
+@OAuthScopes(COMPUTE_READONLY_SCOPE)
 public interface DiskTypeApi {
 
    /** Returns a disk type by name or null if not found. */
    @Named("DiskTypes:get")
    @GET
    @Path("/{diskType}")
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
    @Fallback(NullOnNotFoundOr404.class)
    DiskType get(@PathParam("diskType") String diskType);
 
@@ -63,36 +64,41 @@ public interface DiskTypeApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("DiskTypes:list")
    @GET
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseDiskTypes.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<DiskType> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<DiskType> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("DiskTypes:list")
    @GET
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseDiskTypes.class)
-   @Transform(ParseDiskTypes.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(DiskTypePages.class)
    Iterator<ListPage<DiskType>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("DiskTypes:list")
    @GET
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseDiskTypes.class)
-   @Transform(ParseDiskTypes.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(DiskTypePages.class)
    Iterator<ListPage<DiskType>> list(ListOptions options);
+
+   static final class DiskTypePages extends BaseCallerArg0ToIteratorOfListPage<DiskType, DiskTypePages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject DiskTypePages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<DiskType>> fetchNextPage(final String zoneName, final ListOptions options) {
+         return new Function<String, ListPage<DiskType>>() {
+            @Override public ListPage<DiskType> apply(String pageToken) {
+               return api.diskTypesInZone(zoneName).listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/FirewallApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/FirewallApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/FirewallApi.java
index a9d8818..673ec3f 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/FirewallApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/FirewallApi.java
@@ -17,12 +17,13 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import java.net.URI;
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -35,14 +36,13 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.binders.FirewallBinder;
 import org.jclouds.googlecomputeengine.domain.Firewall;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.PATCH;
-import org.jclouds.googlecomputeengine.functions.internal.ParseFirewalls;
+import org.jclouds.googlecomputeengine.internal.BaseToIteratorOfListPage;
+import org.jclouds.googlecomputeengine.internal.PATCH;
 import org.jclouds.googlecomputeengine.options.FirewallOptions;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
@@ -53,11 +53,12 @@ import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/firewalls")
@@ -136,36 +137,43 @@ public interface FirewallApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("Firewalls:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseFirewalls.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<Firewall> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<Firewall> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Firewalls:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseFirewalls.class)
-   @Transform(ParseFirewalls.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(FirewallPages.class)
    Iterator<ListPage<Firewall>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Firewalls:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseFirewalls.class)
-   @Transform(ParseFirewalls.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(FirewallPages.class)
    Iterator<ListPage<Firewall>> list(ListOptions options);
+
+   static final class FirewallPages extends BaseToIteratorOfListPage<Firewall, FirewallPages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject FirewallPages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override protected Function<String, ListPage<Firewall>> fetchNextPage(final ListOptions options) {
+         return new Function<String, ListPage<Firewall>>() {
+            @Override public ListPage<Firewall> apply(String pageToken) {
+               return api.firewalls().listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ForwardingRuleApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ForwardingRuleApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ForwardingRuleApi.java
index 5e8b4ab..1991226 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ForwardingRuleApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ForwardingRuleApi.java
@@ -17,12 +17,13 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import java.net.URI;
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -34,13 +35,12 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.binders.ForwardingRuleCreationBinder;
 import org.jclouds.googlecomputeengine.domain.ForwardingRule;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseForwardingRules;
+import org.jclouds.googlecomputeengine.internal.BaseCallerArg0ToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ForwardingRuleCreationOptions;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
@@ -50,11 +50,12 @@ import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticator.class)
 @Path("/forwardingRules")
@@ -119,36 +120,46 @@ public interface ForwardingRuleApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("ForwardingRules:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseForwardingRules.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<ForwardingRule> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<ForwardingRule> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("ForwardingRules:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseForwardingRules.class)
-   @Transform(ParseForwardingRules.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(ForwardingRulePages.class)
    Iterator<ListPage<ForwardingRule>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("ForwardingRules:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseForwardingRules.class)
-   @Transform(ParseForwardingRules.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(ForwardingRulePages.class)
    Iterator<ListPage<ForwardingRule>> list(ListOptions options);
+
+   static final class ForwardingRulePages
+         extends BaseCallerArg0ToIteratorOfListPage<ForwardingRule, ForwardingRulePages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject ForwardingRulePages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<ForwardingRule>> fetchNextPage(final String regionName,
+            final ListOptions options) {
+         return new Function<String, ListPage<ForwardingRule>>() {
+            @Override public ListPage<ForwardingRule> apply(String pageToken) {
+               return api.forwardingRulesInRegion(regionName).listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/HttpHealthCheckApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/HttpHealthCheckApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/HttpHealthCheckApi.java
index 1e61ce3..1dc88c5 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/HttpHealthCheckApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/HttpHealthCheckApi.java
@@ -17,11 +17,12 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -34,13 +35,12 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.binders.HttpHealthCheckCreationBinder;
 import org.jclouds.googlecomputeengine.domain.HttpHealthCheck;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseHttpHealthChecks;
+import org.jclouds.googlecomputeengine.internal.BaseToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.HttpHealthCheckCreationOptions;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
@@ -51,11 +51,12 @@ import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PATCH;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticator.class)
 @Path("/httpHealthChecks")
@@ -149,36 +150,43 @@ public interface HttpHealthCheckApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("HttpHealthChecks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseHttpHealthChecks.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<HttpHealthCheck> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<HttpHealthCheck> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("HttpHealthChecks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseHttpHealthChecks.class)
-   @Transform(ParseHttpHealthChecks.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(HttpHealthCheckPages.class)
    Iterator<ListPage<HttpHealthCheck>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("HttpHealthChecks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseHttpHealthChecks.class)
-   @Transform(ParseHttpHealthChecks.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(HttpHealthCheckPages.class)
    Iterator<ListPage<HttpHealthCheck>> list(ListOptions options);
+
+   static final class HttpHealthCheckPages extends BaseToIteratorOfListPage<HttpHealthCheck, HttpHealthCheckPages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject HttpHealthCheckPages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override protected Function<String, ListPage<HttpHealthCheck>> fetchNextPage(final ListOptions options) {
+         return new Function<String, ListPage<HttpHealthCheck>>() {
+            @Override public ListPage<HttpHealthCheck> apply(String pageToken) {
+               return api.httpHeathChecks().listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ImageApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ImageApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ImageApi.java
index 2b1340c..996f9fc 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ImageApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ImageApi.java
@@ -17,11 +17,13 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
+import java.net.URI;
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -33,35 +35,47 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.config.CurrentProject;
 import org.jclouds.googlecomputeengine.domain.Image;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseImages;
+import org.jclouds.googlecomputeengine.internal.BaseArg0ToIteratorOfListPage;
+import org.jclouds.googlecomputeengine.internal.BaseToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.oauth.v2.config.OAuthScopes;
 import org.jclouds.oauth.v2.filters.OAuthAuthenticationFilter;
+import org.jclouds.rest.annotations.Endpoint;
+import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
-@Path("/images")
 @Consumes(APPLICATION_JSON)
 public interface ImageApi {
 
-   /** Returns an image by name or null if not found. */
+   /** Returns an image by self-link or null if not found. */
    @Named("Images:get")
    @GET
-   @Path("/{image}")
+   @OAuthScopes(COMPUTE_READONLY_SCOPE)
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   Image get(@EndpointParam URI selfLink);
+
+   /** Returns an image by name or null if not found. */
+   @Named("Images:get ")
+   @GET
+   @Endpoint(CurrentProject.class)
+   @Path("/global/images/{image}")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
    @Fallback(NullOnNotFoundOr404.class)
    @Nullable
@@ -70,7 +84,8 @@ public interface ImageApi {
    /** Deletes an image by name and returns the operation in progress, or null if not found. */
    @Named("Images:delete")
    @DELETE
-   @Path("/{image}")
+   @Endpoint(CurrentProject.class)
+   @Path("/global/images/{image}")
    @OAuthScopes(COMPUTE_SCOPE)
    @Fallback(NullOnNotFoundOr404.class)
    @Nullable
@@ -86,6 +101,8 @@ public interface ImageApi {
     */
    @Named("Images:insert")
    @POST
+   @Endpoint(CurrentProject.class)
+   @Path("/global/images")
    @Produces(APPLICATION_JSON)
    @OAuthScopes(COMPUTE_SCOPE)
    @MapBinder(BindToJsonPayload.class)
@@ -96,36 +113,103 @@ public interface ImageApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
+    * @param listOptions listing options
+    * @return a page of the list
+    */
+   @Named("Images:list")
+   @GET
+   @Endpoint(CurrentProject.class)
+   @Path("/global/images")
+   @OAuthScopes(COMPUTE_READONLY_SCOPE)
+   ListPage<Image> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
+
+   /** @see #listPage(String, ListOptions) */
+   @Named("Images:list")
+   @GET
+   @Endpoint(CurrentProject.class)
+   @Path("/global/images")
+   @OAuthScopes(COMPUTE_READONLY_SCOPE)
+   @Transform(ImagePages.class)
+   Iterator<ListPage<Image>> list();
+
+   /** @see #listPage(String, ListOptions) */
+   @Named("Images:list")
+   @GET
+   @Endpoint(CurrentProject.class)
+   @Path("/global/images")
+   @OAuthScopes(COMPUTE_READONLY_SCOPE)
+   @Transform(ImagePages.class)
+   Iterator<ListPage<Image>> list(ListOptions options);
+
+   static final class ImagePages extends BaseToIteratorOfListPage<Image, ImagePages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject ImagePages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override protected Function<String, ListPage<Image>> fetchNextPage(final ListOptions options) {
+         return new Function<String, ListPage<Image>>() {
+            @Override public ListPage<Image> apply(String pageToken) {
+               return api.images().listPage(pageToken, options);
+            }
+         };
+      }
+   }
+
+   /**
+    * Retrieves the list of image resources available to the specified project.
+    * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
+    * been set.
+    *
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("Images:list")
    @GET
+   @Path("/projects/{project}/global/images")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseImages.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<Image> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<Image> listPageInProject(@PathParam("project") String projectName,
+         @Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
    /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
+    * @see #listPageInProject(String, String, ListOptions)
     */
    @Named("Images:list")
    @GET
+   @Path("/projects/{project}/global/images")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseImages.class)
-   @Transform(ParseImages.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
-   Iterator<ListPage<Image>> list();
+   @Transform(ImagePagesInProject.class)
+   Iterator<ListPage<Image>> listInProject(@PathParam("project") String projectName);
 
    /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
+    * @see #listPageInProject(String, String, ListOptions)
     */
    @Named("Images:list")
    @GET
+   @Path("/projects/{project}/global/images")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseImages.class)
-   @Transform(ParseImages.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
-   Iterator<ListPage<Image>> list(ListOptions options);
+   @Transform(ImagePagesInProject.class)
+   Iterator<ListPage<Image>> listInProject(@PathParam("project") String projectName, ListOptions options);
+
+   static final class ImagePagesInProject extends BaseArg0ToIteratorOfListPage<Image, ImagePagesInProject> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject ImagePagesInProject(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<Image>> fetchNextPage(final String projectName, final ListOptions options) {
+         return new Function<String, ListPage<Image>>() {
+            @Override public ListPage<Image> apply(String pageToken) {
+               return api.images().listPageInProject(projectName, pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
index 5c107fa..9e6dfd2 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
@@ -17,13 +17,14 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 import static org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig;
 import static org.jclouds.googlecomputeengine.domain.Instance.SerialPortOutput;
 
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -35,14 +36,13 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.domain.Instance;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Metadata;
 import org.jclouds.googlecomputeengine.domain.NewInstance;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseInstances;
+import org.jclouds.googlecomputeengine.internal.BaseCallerArg0ToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.AttachDiskOptions;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
@@ -53,11 +53,12 @@ import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/instances")
@@ -96,44 +97,6 @@ public interface InstanceApi {
    Operation delete(@PathParam("instance") String instance);
 
    /**
-    * Retrieves the list of instance resources available to the specified project.
-    * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
-    * been set.
-    *
-    * @param token       marks the beginning of the next list page
-    * @param listOptions listing options
-    * @return a page of the list
-    */
-   @Named("Instances:list")
-   @GET
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseInstances.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<Instance> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
-
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
-   @Named("Instances:list")
-   @GET
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseInstances.class)
-   @Transform(ParseInstances.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
-   Iterator<ListPage<Instance>> list();
-
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
-   @Named("Instances:list")
-   @GET
-   @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseInstances.class)
-   @Transform(ParseInstances.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
-   Iterator<ListPage<Instance>> list(ListOptions options);
-
-   /**
     * Adds an access config to an instance's network interface.
     *
     * @param instance         the instance name.
@@ -269,5 +232,51 @@ public interface InstanceApi {
    Operation setTags(@PathParam("instance") String instance,
                      @PayloadParam("items") Iterable<String> items,
                      @PayloadParam("fingerprint") String fingerprint);
+
+   /**
+    * Retrieves the list of instance resources available to the specified project.
+    * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
+    * been set.
+    *
+    * @param pageToken   marks the beginning of the next list page
+    * @param listOptions listing options
+    * @return a page of the list
+    */
+   @Named("Instances:list")
+   @GET
+   @OAuthScopes(COMPUTE_READONLY_SCOPE)
+   ListPage<Instance> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
+
+   /** @see #listPage(String, ListOptions) */
+   @Named("Instances:list")
+   @GET
+   @OAuthScopes(COMPUTE_READONLY_SCOPE)
+   @Transform(InstancePages.class)
+   Iterator<ListPage<Instance>> list();
+
+   /** @see #listPage(String, ListOptions) */
+   @Named("Instances:list")
+   @GET
+   @OAuthScopes(COMPUTE_READONLY_SCOPE)
+   @Transform(InstancePages.class)
+   Iterator<ListPage<Instance>> list(ListOptions options);
+
+   static final class InstancePages extends BaseCallerArg0ToIteratorOfListPage<Instance, InstancePages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject InstancePages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<Instance>> fetchNextPage(final String zoneName, final ListOptions options) {
+         return new Function<String, ListPage<Instance>>() {
+            @Override public ListPage<Instance> apply(String pageToken) {
+               return api.instancesInZone(zoneName).listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/MachineTypeApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/MachineTypeApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/MachineTypeApi.java
index b197ab2..f2330b2 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/MachineTypeApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/MachineTypeApi.java
@@ -17,10 +17,11 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
 
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -29,21 +30,21 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.MachineType;
-import org.jclouds.googlecomputeengine.functions.internal.ParseMachineTypes;
+import org.jclouds.googlecomputeengine.internal.BaseCallerArg0ToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.oauth.v2.config.OAuthScopes;
 import org.jclouds.oauth.v2.filters.OAuthAuthenticationFilter;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/machineTypes")
@@ -63,36 +64,44 @@ public interface MachineTypeApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("MachineTypes:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseMachineTypes.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<MachineType> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<MachineType> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("MachineTypes:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseMachineTypes.class)
-   @Transform(ParseMachineTypes.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(MachineTypePages.class)
    Iterator<ListPage<MachineType>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("MachineTypes:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseMachineTypes.class)
-   @Transform(ParseMachineTypes.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(MachineTypePages.class)
    Iterator<ListPage<MachineType>> list(ListOptions options);
+
+   static final class MachineTypePages extends BaseCallerArg0ToIteratorOfListPage<MachineType, MachineTypePages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject MachineTypePages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<MachineType>> fetchNextPage(final String zoneName, final ListOptions options) {
+         return new Function<String, ListPage<MachineType>>() {
+            @Override public ListPage<MachineType> apply(String pageToken) {
+               return api.machineTypesInZone(zoneName).listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java
index ed917a2..85a96bf 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java
@@ -17,11 +17,12 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -33,12 +34,11 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Network;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseNetworks;
+import org.jclouds.googlecomputeengine.internal.BaseToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.oauth.v2.config.OAuthScopes;
@@ -47,11 +47,12 @@ import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.MapBinder;
 import org.jclouds.rest.annotations.PayloadParam;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 import org.jclouds.rest.binders.BindToJsonPayload;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Path("/networks")
@@ -113,36 +114,43 @@ public interface NetworkApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("Networks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseNetworks.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<Network> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<Network> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Networks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseNetworks.class)
-   @Transform(ParseNetworks.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(NetworkPages.class)
    Iterator<ListPage<Network>> list();
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("Networks:list")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseNetworks.class)
-   @Transform(ParseNetworks.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(NetworkPages.class)
    Iterator<ListPage<Network>> list(ListOptions options);
+
+   static final class NetworkPages extends BaseToIteratorOfListPage<Network, NetworkPages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject NetworkPages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override protected Function<String, ListPage<Network>> fetchNextPage(final ListOptions options) {
+         return new Function<String, ListPage<Network>>() {
+            @Override public ListPage<Network> apply(String pageToken) {
+               return api.networks().listPage(pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/OperationApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/OperationApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/OperationApi.java
index 49bbaf9..734bd06 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/OperationApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/OperationApi.java
@@ -18,12 +18,13 @@ package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.jclouds.Fallbacks.VoidOnNotFoundOr404;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import java.net.URI;
 import java.util.Iterator;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -33,24 +34,25 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 
 import org.jclouds.Fallbacks.NullOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyIteratorOnNotFoundOr404;
-import org.jclouds.googlecomputeengine.GoogleComputeEngineFallbacks.EmptyListPageOnNotFoundOr404;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.config.CurrentProject;
 import org.jclouds.googlecomputeengine.domain.ListPage;
 import org.jclouds.googlecomputeengine.domain.Operation;
-import org.jclouds.googlecomputeengine.functions.internal.ParseGlobalOperations;
-import org.jclouds.googlecomputeengine.functions.internal.ParseRegionOperations;
-import org.jclouds.googlecomputeengine.functions.internal.ParseZoneOperations;
+import org.jclouds.googlecomputeengine.internal.BaseArg0ToIteratorOfListPage;
+import org.jclouds.googlecomputeengine.internal.BaseToIteratorOfListPage;
 import org.jclouds.googlecomputeengine.options.ListOptions;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.oauth.v2.config.OAuthScopes;
 import org.jclouds.oauth.v2.filters.OAuthAuthenticationFilter;
+import org.jclouds.rest.annotations.Endpoint;
 import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.RequestFilters;
-import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.rest.annotations.SkipEncoding;
 import org.jclouds.rest.annotations.Transform;
 
+import com.google.common.base.Function;
+
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
 @Consumes(APPLICATION_JSON)
@@ -76,123 +78,156 @@ public interface OperationApi {
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("GlobalOperations:list")
    @GET
-   @Path("/projects/{project}/global/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/global/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseGlobalOperations.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
-   ListPage<Operation> listPage(@Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+   ListPage<Operation> listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #list(org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("GlobalOperations:list")
    @GET
-   @Path("/projects/{project}/global/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/global/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseGlobalOperations.class)
-   @Transform(ParseGlobalOperations.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(OperationPages.class)
    Iterator<ListPage<Operation>> list();
 
-   /**
-    * @see #listPage(String, ListOptions)
-    */
+   /** @see #listPage(String, ListOptions) */
    @Named("GlobalOperations:list")
    @GET
-   @Path("/projects/{project}/global/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/global/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseGlobalOperations.class)
-   @Transform(ParseGlobalOperations.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(OperationPages.class)
    Iterator<ListPage<Operation>> list(ListOptions options);
 
+   static final class OperationPages extends BaseToIteratorOfListPage<Operation, OperationPages> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject OperationPages(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override protected Function<String, ListPage<Operation>> fetchNextPage(final ListOptions options) {
+         return new Function<String, ListPage<Operation>>() {
+            @Override public ListPage<Operation> apply(String pageToken) {
+               return api.operations().listPage(pageToken, options);
+            }
+         };
+      }
+   }
+
    /**
     * Retrieves the list of operation resources available in the specified region.
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("RegionOperations:list")
    @GET
-   @Path("/projects/{project}/regions/{region}/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/regions/{region}/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseRegionOperations.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
    ListPage<Operation> listPageInRegion(@PathParam("region") String region,
-                                        @Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+                                        @Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #listInRegion(String, org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listInRegion(String, org.jclouds.googlecomputeengine.options.ListOptions) */
    @Named("RegionOperations:list")
    @GET
-   @Path("/projects/{project}/regions/{region}/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/regions/{region}/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseRegionOperations.class)
-   @Transform(ParseRegionOperations.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(OperationPagesInRegion.class)
    Iterator<ListPage<Operation>> listInRegion(@PathParam("region") String region);
 
-   /**
-    * @see #listPageInRegion(String, String, ListOptions)
-    */
+   /** @see #listInRegion(String, org.jclouds.googlecomputeengine.options.ListOptions) */
    @Named("RegionOperations:list")
    @GET
-   @Path("/projects/{project}/regions/{region}/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/regions/{region}/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseRegionOperations.class)
-   @Transform(ParseRegionOperations.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(OperationPagesInRegion.class)
    Iterator<ListPage<Operation>> listInRegion(@PathParam("region") String region, ListOptions options);
 
+   static final class OperationPagesInRegion extends BaseArg0ToIteratorOfListPage<Operation, OperationPagesInRegion> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject OperationPagesInRegion(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<Operation>> fetchNextPage(final String regionName,
+            final ListOptions options) {
+         return new Function<String, ListPage<Operation>>() {
+            @Override public ListPage<Operation> apply(String pageToken) {
+               return api.operations().listPageInRegion(regionName, pageToken, options);
+            }
+         };
+      }
+   }
+
    /**
     * Retrieves the list of operation resources available in the specified zone.
     * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not
     * been set.
     *
-    * @param token       marks the beginning of the next list page
+    * @param pageToken   marks the beginning of the next list page
     * @param listOptions listing options
     * @return a page of the list
     */
    @Named("ZoneOperations:list")
    @GET
-   @Path("/projects/{project}/zones/{zone}/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/zones/{zone}/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseZoneOperations.class)
-   @Fallback(EmptyListPageOnNotFoundOr404.class)
    ListPage<Operation> listPageInZone(@PathParam("zone") String zone,
-         @Nullable @QueryParam("pageToken") String token, ListOptions listOptions);
+         @Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions);
 
-   /**
-    * @see #listInZone(String, org.jclouds.googlecomputeengine.options.ListOptions)
-    */
+   /** @see #listInZone(String, org.jclouds.googlecomputeengine.options.ListOptions) */
    @Named("ZoneOperations:list")
    @GET
-   @Path("/projects/{project}/zones/{zone}/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/zones/{zone}/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseZoneOperations.class)
-   @Transform(ParseZoneOperations.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(OperationPagesInZone.class)
    Iterator<ListPage<Operation>> listInZone(@PathParam("zone") String zone);
 
-   /**
-    * @see #listPageInZone(String, String, ListOptions)
-    */
+   /** @see #listInZone(String, org.jclouds.googlecomputeengine.options.ListOptions) */
    @Named("ZoneOperations:list")
    @GET
-   @Path("/projects/{project}/zones/{zone}/operations")
+   @Endpoint(CurrentProject.class)
+   @Path("/zones/{zone}/operations")
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
-   @ResponseParser(ParseZoneOperations.class)
-   @Transform(ParseZoneOperations.ToIteratorOfListPage.class)
-   @Fallback(EmptyIteratorOnNotFoundOr404.class)
+   @Transform(OperationPagesInZone.class)
    Iterator<ListPage<Operation>> listInZone(@PathParam("zone") String zone, ListOptions options);
+
+   static final class OperationPagesInZone extends BaseArg0ToIteratorOfListPage<Operation, OperationPagesInZone> {
+
+      private final GoogleComputeEngineApi api;
+
+      @Inject OperationPagesInZone(GoogleComputeEngineApi api) {
+         this.api = api;
+      }
+
+      @Override
+      protected Function<String, ListPage<Operation>> fetchNextPage(final String zoneName, final ListOptions options) {
+         return new Function<String, ListPage<Operation>>() {
+            @Override public ListPage<Operation> apply(String pageToken) {
+               return api.operations().listPageInZone(zoneName, pageToken, options);
+            }
+         };
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/37e0397d/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ProjectApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ProjectApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ProjectApi.java
index cc10657..b6242c6 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ProjectApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/ProjectApi.java
@@ -17,15 +17,14 @@
 package org.jclouds.googlecomputeengine.features;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_READONLY_SCOPE;
-import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.COMPUTE_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_READONLY_SCOPE;
+import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineScopes.COMPUTE_SCOPE;
 
 import javax.inject.Named;
 import javax.ws.rs.Consumes;
 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.googlecomputeengine.GoogleComputeEngineFallbacks.NullOn400or404;
@@ -42,20 +41,18 @@ import org.jclouds.rest.binders.BindToJsonPayload;
 
 @SkipEncoding({'/', '='})
 @RequestFilters(OAuthAuthenticationFilter.class)
-@Path("/projects")
 @Consumes(APPLICATION_JSON)
 public interface ProjectApi {
 
-   /** Returns a project by name or null if not found. */
+   /** Get the current project. */
    @Named("Projects:get")
    @GET
    @OAuthScopes(COMPUTE_READONLY_SCOPE)
    @Fallback(NullOn400or404.class)
-   @Path("/{project}")
-   Project get(@PathParam("project") String projectName);
+   Project get();
 
    /**
-    * Sets metadata common to all instances within the specified project using the data included in the request.
+    * Sets metadata common to all instances within the current project using the data included in the request.
     * <p/>
     * NOTE: This *sets* metadata items on the project (vs *adding* items to metadata),
     * if there are existing metadata that must be kept these must be fetched first and then re-sent on update.
@@ -65,16 +62,14 @@ public interface ProjectApi {
     *    projectApi.setCommonInstanceMetadata("myProject", update);
     * </tt></pre>
     *
-    * @param projectName   name of the project to return
     * @param metadata      the metadata to set
     * @return an Operations resource. To check on the status of an operation, poll the Operations resource returned
     *         to you, and look for the status field.
     */
    @Named("Projects:setCommonInstanceMetadata")
    @POST
-   @Path("/{project}/setCommonInstanceMetadata")
+   @Path("/setCommonInstanceMetadata")
    @OAuthScopes(COMPUTE_SCOPE)
    @Produces(APPLICATION_JSON)
-   Operation setCommonInstanceMetadata(@PathParam("project") String projectName,
-                                       @BinderParam(BindToJsonPayload.class) Metadata metadata);
+   Operation setCommonInstanceMetadata(@BinderParam(BindToJsonPayload.class) Metadata metadata);
 }