diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Option.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Option.java
new file mode 100644
index 000000000000..853b678a2ebe
--- /dev/null
+++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Option.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.resourcemanager;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.gcloud.spi.ResourceManagerRpc;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Base class for Resource Manager operation options
+ */
+class Option implements Serializable {
+
+ private static final long serialVersionUID = 2655177550880762967L;
+
+ private final ResourceManagerRpc.Option rpcOption;
+ private final Object value;
+
+ Option(ResourceManagerRpc.Option rpcOption, Object value) {
+ this.rpcOption = checkNotNull(rpcOption);
+ this.value = value;
+ }
+
+ ResourceManagerRpc.Option rpcOption() {
+ return rpcOption;
+ }
+
+ Object value() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Option)) {
+ return false;
+ }
+ Option other = (Option) obj;
+ return Objects.equals(rpcOption, other.rpcOption)
+ && Objects.equals(value, other.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rpcOption, value);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("name", rpcOption.value())
+ .add("value", value)
+ .toString();
+ }
+}
diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Project.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Project.java
new file mode 100644
index 000000000000..a08d7e3714be
--- /dev/null
+++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/Project.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gcloud.resourcemanager;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A Google Cloud Resource Manager project object.
+ *
+ * A Project is a high-level Google Cloud Platform entity. It is a container for ACLs, APIs,
+ * AppEngine Apps, VMs, and other Google Cloud Platform resources. This class' member variables are
+ * immutable. Methods that change or update the underlying Project information return a new Project
+ * instance.
+ */
+public class Project {
+
+ private final ResourceManager resourceManager;
+ private final ProjectInfo info;
+
+ /**
+ * Constructs a Project object that contains the ProjectInfo given.
+ */
+ public Project(ResourceManager resourceManager, ProjectInfo projectInfo) {
+ this.resourceManager = checkNotNull(resourceManager);
+ this.info = checkNotNull(projectInfo);
+ }
+
+ /**
+ * Constructs a Project object that contains project information loaded from the server.
+ *
+ * @return Project object containing the project's metadata
+ * @throws ResourceManagerException upon failure
+ */
+ public static Project load(ResourceManager resourceManager, String projectId) {
+ ProjectInfo projectInfo = resourceManager.get(projectId);
+ return new Project(resourceManager, projectInfo);
+ }
+
+ /**
+ * Returns the {@link ProjectInfo} object associated with this Project.
+ */
+ public ProjectInfo info() {
+ return info;
+ }
+
+ /**
+ * Returns the {@link ResourceManager} service object associated with this Project.
+ */
+ public ResourceManager resourceManager() {
+ return resourceManager;
+ }
+
+ /**
+ * Returns a Project object with updated project information.
+ *
+ * @return Project object containing the project's updated metadata
+ * @throws ResourceManagerException upon failure
+ */
+ public Project reload() {
+ return Project.load(resourceManager, info.projectId());
+ }
+
+ /**
+ * Marks the project identified by the specified project ID for deletion.
+ *
+ * This method will only affect the project if the following criteria are met:
+ *
+ *
The project does not have a billing account associated with it.
+ *
The project has a lifecycle state of {@link ProjectInfo.State#ACTIVE}.
+ *
+ * This method changes the project's lifecycle state from {@link ProjectInfo.State#ACTIVE} to
+ * {@link ProjectInfo.State#DELETE_REQUESTED}. The deletion starts at an unspecified time, at
+ * which point the lifecycle state changes to {@link ProjectInfo.State#DELETE_IN_PROGRESS}. Until
+ * the deletion completes, you can check the lifecycle state checked by retrieving the project
+ * with {@link ResourceManager#get}, and the project remains visible to
+ * {@link ResourceManager#list}. However, you cannot update the project. After the deletion
+ * completes, the project is not retrievable by the {@link ResourceManager#get} and
+ * {@link ResourceManager#list} methods. The caller must have modify permissions for this project.
+ *
+ * @see Cloud
+ * Resource Manager delete
+ * @throws ResourceManagerException upon failure
+ */
+ public void delete() {
+ resourceManager.delete(info.projectId());
+ }
+
+ /**
+ * Restores the project identified by the specified project ID.
+ *
+ * You can only use this method for a project that has a lifecycle state of
+ * {@link ProjectInfo.State#DELETE_REQUESTED}. After deletion starts, as indicated by a lifecycle
+ * state of {@link ProjectInfo.State#DELETE_IN_PROGRESS}, the project cannot be restored. The
+ * caller must have modify permissions for this project.
+ *
+ * @see Cloud
+ * Resource Manager undelete
+ * @throws ResourceManagerException upon failure (including when the project can't be restored)
+ */
+ public void undelete() {
+ resourceManager.undelete(info.projectId());
+ }
+
+ /**
+ * Replaces the attributes of the project.
+ *
+ * The caller must have modify permissions for this project.
+ *
+ * @see Cloud
+ * Resource Manager update
+ * @return the ProjectInfo representing the new project metadata
+ * @throws ResourceManagerException upon failure
+ */
+ public Project replace(ProjectInfo projectInfo) {
+ return new Project(resourceManager, resourceManager.replace(checkNotNull(projectInfo)));
+ }
+}
diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java
new file mode 100644
index 000000000000..5d490e662df0
--- /dev/null
+++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ProjectInfo.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gcloud.resourcemanager;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.client.util.Data;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A Google Cloud Resource Manager project metadata object.
+ *
+ * A Project is a high-level Google Cloud Platform entity. It is a container for ACLs, APIs,
+ * AppEngine Apps, VMs, and other Google Cloud Platform resources.
+ */
+public class ProjectInfo implements Serializable {
+
+ private static final long serialVersionUID = 9148970963697734236L;
+ private final String name;
+ private final String projectId;
+ private final Map labels;
+ private final Long projectNumber;
+ private final State state;
+ private final Long createTimeMillis;
+ private final ResourceId parent;
+
+ /**
+ * The project lifecycle states.
+ */
+ public enum State {
+ /**
+ * Only used/useful for distinguishing unset values
+ */
+ LIFECYCLE_STATE_UNSPECIFIED,
+
+ /**
+ * The normal and active state
+ */
+ ACTIVE,
+
+ /**
+ * The project has been marked for deletion by the user or by the system (Google Cloud
+ * Platform). This can generally be reversed by calling {@link ResourceManager#undelete}.
+ */
+ DELETE_REQUESTED,
+
+ /**
+ * the process of deleting the project has begun. Reversing the deletion is no longer possible.
+ */
+ DELETE_IN_PROGRESS
+ }
+
+ static class ResourceId implements Serializable {
+
+ private static final long serialVersionUID = -325199985993344726L;
+
+ private final String id;
+ private final String type;
+
+ ResourceId(String id, String type) {
+ this.id = checkNotNull(id);
+ this.type = checkNotNull(type);
+ }
+
+ String id() {
+ return id;
+ }
+
+ String type() {
+ return type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof ResourceId && Objects.equals(toPb(), ((ResourceId) obj).toPb());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, type);
+ }
+
+ com.google.api.services.cloudresourcemanager.model.ResourceId toPb() {
+ com.google.api.services.cloudresourcemanager.model.ResourceId resourceIdPb =
+ new com.google.api.services.cloudresourcemanager.model.ResourceId();
+ resourceIdPb.setId(id);
+ resourceIdPb.setType(type.toString().toLowerCase());
+ return resourceIdPb;
+ }
+
+ static ResourceId fromPb(
+ com.google.api.services.cloudresourcemanager.model.ResourceId resourceIdPb) {
+ return new ResourceId(resourceIdPb.getId(), resourceIdPb.getType());
+ }
+ }
+
+ public static class Builder {
+
+ private String name;
+ private String projectId;
+ private Map labels = new HashMap<>();
+ private Long projectNumber;
+ private State state;
+ private Long createTimeMillis;
+ private ResourceId parent;
+
+ private Builder() {
+ }
+
+ Builder(ProjectInfo info) {
+ this.name = info.name;
+ this.projectId = info.projectId;
+ this.labels.putAll(info.labels);
+ this.projectNumber = info.projectNumber;
+ this.state = info.state;
+ this.createTimeMillis = info.createTimeMillis;
+ this.parent = info.parent;
+ }
+
+ /**
+ * Set the user-assigned name of the project.
+ *
+ * This field is optional and can remain unset. Allowed characters are: lowercase and uppercase
+ * letters, numbers, hyphen, single-quote, double-quote, space, and exclamation point. This
+ * field can be changed after project creation.
+ */
+ public Builder name(String name) {
+ this.name = firstNonNull(name, Data.nullOf(String.class));
+ return this;
+ }
+
+ /**
+ * Set the unique, user-assigned ID of the project.
+ *
+ * The ID must be 6 to 30 lowercase letters, digits, or hyphens. It must start with a letter.
+ * Trailing hyphens are prohibited. This field cannot be changed after the server creates the
+ * project.
+ */
+ public Builder projectId(String projectId) {
+ this.projectId = checkNotNull(projectId);
+ return this;
+ }
+
+ /**
+ * Add a label associated with this project.
+ *
+ * See {@link #labels} for label restrictions.
+ */
+ public Builder addLabel(String key, String value) {
+ this.labels.put(key, value);
+ return this;
+ }
+
+ /**
+ * Remove a label associated with this project.
+ */
+ public Builder removeLabel(String key) {
+ this.labels.remove(key);
+ return this;
+ }
+
+ /**
+ * Clear the labels associated with this project.
+ */
+ public Builder clearLabels() {
+ this.labels.clear();
+ return this;
+ }
+
+ /**
+ * Set the labels associated with this project.
+ *
+ * Label keys must be between 1 and 63 characters long and must conform to the following regular
+ * expression: [a-z]([-a-z0-9]*[a-z0-9])?. Label values must be between 0 and 63 characters long
+ * and must conform to the regular expression ([a-z]([-a-z0-9]*[a-z0-9])?)?. No more than 256
+ * labels can be associated with a given resource. This field can be changed after project
+ * creation.
+ */
+ public Builder labels(Map labels) {
+ this.labels = Maps.newHashMap(checkNotNull(labels));
+ return this;
+ }
+
+ Builder projectNumber(Long projectNumber) {
+ this.projectNumber = projectNumber;
+ return this;
+ }
+
+ Builder state(State state) {
+ this.state = state;
+ return this;
+ }
+
+ Builder createTimeMillis(Long createTimeMillis) {
+ this.createTimeMillis = createTimeMillis;
+ return this;
+ }
+
+ Builder parent(ResourceId parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ public ProjectInfo build() {
+ return new ProjectInfo(this);
+ }
+ }
+
+ ProjectInfo(Builder builder) {
+ this.name = builder.name;
+ this.projectId = builder.projectId;
+ this.labels = ImmutableMap.copyOf(builder.labels);
+ this.projectNumber = builder.projectNumber;
+ this.state = builder.state;
+ this.createTimeMillis = builder.createTimeMillis;
+ this.parent = builder.parent;
+ }
+
+ /**
+ * Get the unique, user-assigned ID of the project.
+ *
+ * This field cannot be changed after the server creates the project.
+ */
+ public String projectId() {
+ return projectId;
+ }
+
+ /**
+ * Get the user-assigned name of the project.
+ *
+ * This field is optional, can remain unset, and can be changed after project creation.
+ */
+ public String name() {
+ return Data.isNull(name) ? null : name;
+ }
+
+ /**
+ * Get number uniquely identifying the project.
+ *
+ * This field is set by the server and is read-only.
+ */
+ public Long projectNumber() {
+ return projectNumber;
+ }
+
+ /**
+ * Get the immutable map of labels associated with this project.
+ */
+ public Map labels() {
+ return labels;
+ }
+
+ /**
+ * Get the project's lifecycle state.
+ *
+ * This is a read-only field. To change the lifecycle state of your project, use the
+ * {@code delete} or {@code undelete} method.
+ */
+ public State state() {
+ return state;
+ }
+
+ ResourceId parent() {
+ return parent;
+ }
+
+ /**
+ * Get the project's creation time (in milliseconds).
+ *
+ * This field is set by the server and is read-only.
+ */
+ public Long createTimeMillis() {
+ return createTimeMillis;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof ProjectInfo && Objects.equals(toPb(), ((ProjectInfo) obj).toPb());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, projectId, labels, projectNumber, state, createTimeMillis, parent);
+ }
+
+ public static Builder builder(String id) {
+ return new Builder().projectId(id);
+ }
+
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ com.google.api.services.cloudresourcemanager.model.Project toPb() {
+ com.google.api.services.cloudresourcemanager.model.Project projectPb =
+ new com.google.api.services.cloudresourcemanager.model.Project();
+ projectPb.setName(name);
+ projectPb.setProjectId(projectId);
+ projectPb.setLabels(labels);
+ projectPb.setProjectNumber(projectNumber);
+ if (state != null) {
+ projectPb.setLifecycleState(state.toString());
+ }
+ if (createTimeMillis != null) {
+ projectPb.setCreateTime(ISODateTimeFormat.dateTime().print(createTimeMillis));
+ }
+ if (parent != null) {
+ projectPb.setParent(parent.toPb());
+ }
+ return projectPb;
+ }
+
+ static ProjectInfo fromPb(com.google.api.services.cloudresourcemanager.model.Project projectPb) {
+ ProjectInfo.Builder builder =
+ ProjectInfo.builder(projectPb.getProjectId()).projectNumber(projectPb.getProjectNumber());
+ if (projectPb.getName() != null) {
+ builder.name(projectPb.getName());
+ }
+ if (projectPb.getLabels() != null) {
+ builder.labels(projectPb.getLabels());
+ }
+ if (projectPb.getLifecycleState() != null) {
+ builder.state(State.valueOf(projectPb.getLifecycleState()));
+ }
+ if (projectPb.getCreateTime() != null) {
+ builder.createTimeMillis(DateTime.parse(projectPb.getCreateTime()).getMillis());
+ }
+ if (projectPb.getParent() != null) {
+ builder.parent(ResourceId.fromPb(projectPb.getParent()));
+ }
+ return builder.build();
+ }
+}
diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java
index 8a9966faa653..1562fe51dad1 100644
--- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java
+++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManager.java
@@ -16,7 +16,13 @@
package com.google.gcloud.resourcemanager;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Sets;
+import com.google.gcloud.Page;
import com.google.gcloud.Service;
+import com.google.gcloud.spi.ResourceManagerRpc;
+
+import java.util.HashSet;
/**
* An interface for Google Cloud Resource Manager.
@@ -27,5 +33,236 @@ public interface ResourceManager extends Service {
public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
- // TODO(ajaykannan): Fix me! Add in missing methods.
+ /**
+ * The fields of a project.
+ *
+ * These values can be used to specify the fields to include in a partial response when calling
+ * {@link ResourceManager#get} or {@link ResourceManager#list}. Project ID is always returned,
+ * even if not specified.
+ */
+ enum ProjectField {
+ PROJECT_ID("projectId"),
+ NAME("name"),
+ LABELS("labels"),
+ PROJECT_NUMBER("projectNumber"),
+ STATE("lifecycleState"),
+ CREATE_TIME("createTime");
+
+ private final String selector;
+
+ ProjectField(String selector) {
+ this.selector = selector;
+ }
+
+ public String selector() {
+ return selector;
+ }
+
+ static String selector(ProjectField... fields) {
+ HashSet fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1);
+ fieldStrings.add(PROJECT_ID.selector());
+ for (ProjectField field : fields) {
+ fieldStrings.add(field.selector());
+ }
+ return Joiner.on(',').join(fieldStrings);
+ }
+ }
+
+ /**
+ * Class for specifying project get options.
+ */
+ public class ProjectGetOption extends Option {
+
+ private static final long serialVersionUID = 270185129961146874L;
+
+ private ProjectGetOption(ResourceManagerRpc.Option option, Object value) {
+ super(option, value);
+ }
+
+ /**
+ * Returns an option to specify the project's fields to be returned by the RPC call.
+ *
+ * If this option is not provided all project fields are returned.
+ * {@code ProjectGetOption.fields} can be used to specify only the fields of interest. Project
+ * ID is always returned, even if not specified. {@link ProjectField} provides a list of fields
+ * that can be used.
+ */
+ public static ProjectGetOption fields(ProjectField... fields) {
+ return new ProjectGetOption(ResourceManagerRpc.Option.FIELDS, ProjectField.selector(fields));
+ }
+ }
+
+ /**
+ * Class for specifying project list options.
+ */
+ public class ProjectListOption extends Option {
+
+ private static final long serialVersionUID = 7888768979702012328L;
+
+ private ProjectListOption(ResourceManagerRpc.Option option, Object value) {
+ super(option, value);
+ }
+
+ /**
+ * Returns an option to specify a filter.
+ *
+ * Filter rules are case insensitive. The fields eligible for filtering are:
+ *
+ *
name
+ *
project ID
+ *
labels.key, where key is the name of a label
+ *
+ *
+ * You can specify multiple filters by adding a space between each filter. Multiple filters
+ * are composed using "and".
+ *
+ * Some examples of filters:
+ *
+ *
name:* The project has a name.
+ *
name:Howl The project's name is Howl or howl.
+ *
name:HOWL Equivalent to above.
+ *
NAME:howl Equivalent to above.
+ *
labels.color:* The project has the label color.
+ *
labels.color:red The project's label color has the value red.
+ *
labels.color:red label.size:big The project's label color has the value red and its
+ * label size has the value big.
+ *
+ */
+ public static ProjectListOption filter(String filter) {
+ return new ProjectListOption(ResourceManagerRpc.Option.FILTER, filter);
+ }
+
+ /**
+ * Returns an option to specify a page token.
+ *
+ * The page token (returned from a previous call to list) indicates from where listing should
+ * continue.
+ */
+ public static ProjectListOption pageToken(String pageToken) {
+ return new ProjectListOption(ResourceManagerRpc.Option.PAGE_TOKEN, pageToken);
+ }
+
+ /**
+ * The maximum number of projects to return per RPC.
+ *
+ * The server can return fewer projects than requested. When there are more results than the
+ * page size, the server will return a page token that can be used to fetch other results.
+ * Note: pagination is not yet supported; the server currently ignores this field and returns
+ * all results.
+ */
+ public static ProjectListOption pageSize(int pageSize) {
+ return new ProjectListOption(ResourceManagerRpc.Option.PAGE_SIZE, pageSize);
+ }
+
+ /**
+ * Returns an option to specify the project's fields to be returned by the RPC call.
+ *
+ * If this option is not provided all project fields are returned.
+ * {@code ProjectListOption.fields} can be used to specify only the fields of interest. Project
+ * ID is always returned, even if not specified. {@link ProjectField} provides a list of fields
+ * that can be used.
+ */
+ public static ProjectListOption fields(ProjectField... fields) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("projects(").append(ProjectField.selector(fields)).append(")");
+ return new ProjectListOption(ResourceManagerRpc.Option.FIELDS, builder.toString());
+ }
+ }
+
+ /**
+ * Create a new project.
+ *
+ * Initially, the project resource is owned by its creator exclusively. The creator can later
+ * grant permission to others to read or update the project. Several APIs are activated
+ * automatically for the project, including Google Cloud Storage.
+ *
+ * @see Cloud
+ * Resource Manager create
+ * @return ProjectInfo object representing the new project's metadata. The returned object will
+ * include the following read-only fields supplied by the server: project number, lifecycle
+ * state, and creation time.
+ * @throws ResourceManagerException upon failure
+ */
+ ProjectInfo create(ProjectInfo project);
+
+ /**
+ * Marks the project identified by the specified project ID for deletion.
+ *
+ * This method will only affect the project if the following criteria are met:
+ *
+ *
The project does not have a billing account associated with it.
+ *
The project has a lifecycle state of {@link ProjectInfo.State#ACTIVE}.
+ *
+ * This method changes the project's lifecycle state from {@link ProjectInfo.State#ACTIVE} to
+ * {@link ProjectInfo.State#DELETE_REQUESTED}. The deletion starts at an unspecified time, at
+ * which point the lifecycle state changes to {@link ProjectInfo.State#DELETE_IN_PROGRESS}. Until
+ * the deletion completes, you can check the lifecycle state checked by retrieving the project
+ * with {@link ResourceManager#get}, and the project remains visible to
+ * {@link ResourceManager#list}. However, you cannot update the project. After the deletion
+ * completes, the project is not retrievable by the {@link ResourceManager#get} and
+ * {@link ResourceManager#list} methods. The caller must have modify permissions for this project.
+ *
+ * @see Cloud
+ * Resource Manager delete
+ * @throws ResourceManagerException upon failure
+ */
+ void delete(String projectId);
+
+ /**
+ * Retrieves the project identified by the specified project ID.
+ *
+ * The caller must have read permissions for this project.
+ *
+ * @see Cloud
+ * Resource Manager get
+ * @throws ResourceManagerException upon failure
+ */
+ ProjectInfo get(String projectId, ProjectGetOption... options);
+
+ /**
+ * Lists the projects visible to the current user.
+ *
+ * This method returns projects in an unspecified order. New projects do not necessarily appear at
+ * the end of the list. Use {@link ProjectListOption} to filter this list, set page size, and set
+ * page tokens. Note that pagination is currently not implemented by the Cloud Resource Manager
+ * API.
+ *
+ * @see Cloud
+ * Resource Manager list
+ * @return {@code Page}, a page of projects.
+ * @throws ResourceManagerException upon failure
+ */
+ Page list(ProjectListOption... options);
+
+ /**
+ * Replaces the attributes of the project.
+ *
+ * The caller must have modify permissions for this project.
+ *
+ * @see Cloud
+ * Resource Manager update
+ * @return the ProjectInfo representing the new project metadata
+ * @throws ResourceManagerException upon failure
+ */
+ ProjectInfo replace(ProjectInfo newProject);
+
+ /**
+ * Restores the project identified by the specified project ID.
+ *
+ * You can only use this method for a project that has a lifecycle state of
+ * {@link ProjectInfo.State#DELETE_REQUESTED}. After deletion starts, as indicated by a lifecycle
+ * state of {@link ProjectInfo.State#DELETE_IN_PROGRESS}, the project cannot be restored. The
+ * caller must have modify permissions for this project.
+ *
+ * @see Cloud
+ * Resource Manager undelete
+ * @throws ResourceManagerException
+ */
+ void undelete(String projectId);
}
diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerException.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerException.java
index e136db8fd339..22b5e8bfed7c 100644
--- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerException.java
+++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerException.java
@@ -16,6 +16,8 @@
package com.google.gcloud.resourcemanager;
+import com.google.gcloud.BaseServiceException;
+import com.google.gcloud.RetryHelper;
import com.google.gcloud.RetryHelper.RetryHelperException;
import com.google.gcloud.RetryHelper.RetryInterruptedException;
@@ -25,29 +27,13 @@
* @see Google Cloud
* Resource Manager error codes
*/
-public class ResourceManagerException extends RuntimeException {
+public class ResourceManagerException extends BaseServiceException {
private static final long serialVersionUID = 6841689911565501705L;
private static final int UNKNOWN_CODE = -1;
- private final int code;
- private final boolean retryable;
-
public ResourceManagerException(int code, String message, boolean retryable) {
- super(message);
- this.code = code;
- this.retryable = retryable;
- }
-
- /**
- * Returns the code associated with this exception.
- */
- public int code() {
- return code;
- }
-
- public boolean retryable() {
- return retryable;
+ super(code, message, retryable);
}
/**
@@ -59,7 +45,12 @@ public boolean retryable() {
* @throws RetryInterruptedException when {@code ex} is a {@code RetryInterruptedException}
*/
static ResourceManagerException translateAndThrow(RetryHelperException ex) {
+ if (ex.getCause() instanceof ResourceManagerException) {
+ throw (ResourceManagerException) ex.getCause();
+ }
+ if (ex instanceof RetryHelper.RetryInterruptedException) {
+ RetryHelper.RetryInterruptedException.propagate();
+ }
throw new ResourceManagerException(UNKNOWN_CODE, ex.getMessage(), false);
- // TODO(ajaykannan): Fix me!
}
}
diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java
index e43609be95c1..990163c459da 100644
--- a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java
+++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/resourcemanager/ResourceManagerOptions.java
@@ -40,6 +40,13 @@ public ResourceManager create(ResourceManagerOptions options) {
}
}
+ /**
+ * Returns a default {@code ResourceManagerOptions} instance.
+ */
+ public static ResourceManagerOptions defaultInstance() {
+ return builder().build();
+ }
+
public static class DefaultResourceManagerRpcFactory implements ResourceManagerRpcFactory {
private static final ResourceManagerRpcFactory INSTANCE =
new DefaultResourceManagerRpcFactory();
@@ -70,6 +77,11 @@ private ResourceManagerOptions(Builder builder) {
super(ResourceManagerFactory.class, ResourceManagerRpcFactory.class, builder);
}
+ @Override
+ protected boolean projectIdRequired() {
+ return false;
+ }
+
@Override
protected ResourceManagerFactory defaultServiceFactory() {
return DefaultResourceManagerFactory.INSTANCE;
diff --git a/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/spi/DefaultResourceManagerRpc.java b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/spi/DefaultResourceManagerRpc.java
new file mode 100644
index 000000000000..d73c6ae4015c
--- /dev/null
+++ b/gcloud-java-resourcemanager/src/main/java/com/google/gcloud/spi/DefaultResourceManagerRpc.java
@@ -0,0 +1,88 @@
+package com.google.gcloud.spi;
+
+import com.google.api.client.googleapis.json.GoogleJsonError;
+import com.google.api.client.googleapis.json.GoogleJsonResponseException;
+import com.google.api.client.http.HttpRequestInitializer;
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.json.jackson.JacksonFactory;
+import com.google.api.services.cloudresourcemanager.Cloudresourcemanager;
+import com.google.api.services.cloudresourcemanager.model.Project;
+import com.google.common.collect.ImmutableSet;
+import com.google.gcloud.resourcemanager.ResourceManagerException;
+import com.google.gcloud.resourcemanager.ResourceManagerOptions;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+public class DefaultResourceManagerRpc implements ResourceManagerRpc {
+
+ // see https://cloud.google.com/resource-manager/v1/errors/core_errors
+ private static final Set RETRYABLE_CODES = ImmutableSet.of(503, 500, 429, 417);
+
+ private final ResourceManagerOptions options;
+ private final Cloudresourcemanager resourceManager;
+
+ public DefaultResourceManagerRpc(ResourceManagerOptions options) {
+ HttpTransport transport = options.httpTransportFactory().create();
+ HttpRequestInitializer initializer = options.httpRequestInitializer();
+ this.options = options;
+ resourceManager =
+ new Cloudresourcemanager.Builder(transport, new JacksonFactory(), initializer)
+ .setRootUrl(options.host())
+ .setApplicationName(options.applicationName())
+ .build();
+ }
+
+ private static ResourceManagerException translate(IOException exception) {
+ ResourceManagerException translated;
+ if (exception instanceof GoogleJsonResponseException) {
+ translated = translate(((GoogleJsonResponseException) exception).getDetails());
+ } else {
+ translated = new ResourceManagerException(0, exception.getMessage(), false);
+ }
+ translated.initCause(exception);
+ return translated;
+ }
+
+ private static ResourceManagerException translate(GoogleJsonError exception) {
+ boolean retryable = RETRYABLE_CODES.contains(exception.getCode());
+ return new ResourceManagerException(exception.getCode(), exception.getMessage(), retryable);
+ }
+
+ @Override
+ public Project create(Project project) throws ResourceManagerException {
+ // TODO(ajaykannan): fix me!
+ return null;
+ }
+
+ @Override
+ public void delete(String projectId) throws ResourceManagerException {
+ // TODO(ajaykannan): fix me!
+ }
+
+ @Override
+ public Project get(String projectId, Map