001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.isis.viewer.restfulobjects.applib;
020
021 import java.io.IOException;
022 import java.util.List;
023
024 import javax.ws.rs.core.Response;
025 import javax.ws.rs.core.Response.Status.Family;
026
027 import com.google.common.collect.Lists;
028
029 import org.codehaus.jackson.JsonParseException;
030 import org.codehaus.jackson.map.JsonMappingException;
031
032 import org.apache.isis.viewer.restfulobjects.applib.links.LinkRepresentation;
033
034 public class RepresentationWalker {
035
036 static class Step {
037 private final String key;
038 private final LinkRepresentation link;
039 private final JsonRepresentation body;
040 private final RestfulResponse<? extends JsonRepresentation> response;
041 private String error;
042 private final Exception exception;
043
044 public Step(final String key, final LinkRepresentation link, final JsonRepresentation body, final RestfulResponse<? extends JsonRepresentation> response, final String error, final Exception exception) {
045 this.key = key;
046 this.link = link;
047 this.body = body;
048 this.response = response;
049 this.error = error;
050 this.exception = exception;
051 }
052
053 @Override
054 public String toString() {
055 return "Step [key=" + key + ", link=" + (link != null ? link.getHref() : "(null)") + ", error=" + error + "]";
056 }
057
058 }
059
060 private final RestfulClient restfulClient;
061 private final List<Step> steps = Lists.newLinkedList();
062
063 public RepresentationWalker(final RestfulClient restfulClient, final Response response) {
064 this.restfulClient = restfulClient;
065 final RestfulResponse<JsonRepresentation> jsonResp = RestfulResponse.of(response);
066
067 addStep(null, null, null, jsonResp, null, null);
068 }
069
070 private Step addStep(final String key, final LinkRepresentation link, final JsonRepresentation body, final RestfulResponse<JsonRepresentation> jsonResp, final String error, final Exception ex) {
071 final Step step = new Step(key, link, body, jsonResp, error, ex);
072 steps.add(0, step);
073 if (error != null) {
074 if (jsonResp.getStatus().getFamily() != Family.SUCCESSFUL) {
075 step.error = "response status code: " + jsonResp.getStatus();
076 }
077 }
078 return step;
079 }
080
081 public void walk(final String path) {
082 walk(path, null);
083 }
084
085 public void walk(final String path, final JsonRepresentation invokeBody) {
086 final Step previousStep = currentStep();
087 if (previousStep.error != null) {
088 return;
089 }
090
091 final RestfulResponse<? extends JsonRepresentation> jsonResponse = previousStep.response;
092 JsonRepresentation entity;
093 try {
094 entity = jsonResponse.getEntity();
095 } catch (final Exception e) {
096 addStep(path, null, null, null, "exception: " + e.getMessage(), e);
097 return;
098 }
099
100 LinkRepresentation link;
101 try {
102 link = entity.getLink(path);
103 } catch (final Exception e) {
104 addStep(path, null, null, null, "exception: " + e.getMessage(), e);
105 return;
106 }
107 if (link == null) {
108 addStep(path, null, null, null, "no such link '" + path + "'", null);
109 return;
110 }
111
112 final RestfulResponse<JsonRepresentation> response;
113 try {
114 if (invokeBody != null) {
115 response = restfulClient.follow(link, invokeBody);
116 } else {
117 response = restfulClient.follow(link);
118 }
119 } catch (final Exception e) {
120 addStep(path, link, null, null, "failed to follow link: " + e.getMessage(), e);
121 return;
122 }
123
124 addStep(path, link, null, response, null, null);
125 }
126
127 /**
128 * The entity returned from the previous walk.
129 *
130 * <p>
131 * Will return null if the previous walk returned an error.
132 */
133 public JsonRepresentation getEntity() throws JsonParseException, JsonMappingException, IOException {
134 final Step currentStep = currentStep();
135 if (currentStep.response == null || currentStep.error != null) {
136 return null;
137 }
138 return currentStep.response.getEntity();
139 }
140
141 /**
142 * The response returned from the previous walk.
143 *
144 * <p>
145 * Once a walk/performed has been attempted, is guaranteed to return a
146 * non-null value. (Conversely, will return <tt>null</tt> immediately after
147 * instantiation and prior to a walk being attempted/performed).
148 */
149 public RestfulResponse<?> getResponse() {
150 final Step currentStep = currentStep();
151 return currentStep != null ? currentStep.response : null;
152 }
153
154 /**
155 * The error (if any) that occurred from the previous walk.
156 */
157 public String getError() {
158 final Step currentStep = currentStep();
159 return currentStep != null ? currentStep.error : null;
160 }
161
162 /**
163 * The exception (if any) that occurred from the previous walk.
164 *
165 * <p>
166 * Will only ever be populated if {@link #getError()} is non-null.
167 */
168 public Exception getException() {
169 final Step currentStep = currentStep();
170 return currentStep != null ? currentStep.exception : null;
171 }
172
173 /**
174 * The step that has just been walked.
175 */
176 private Step currentStep() {
177 return steps.get(0);
178 }
179
180 }