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 */ 019package org.apache.isis.viewer.restfulobjects.applib.client; 020 021import java.io.IOException; 022import java.util.List; 023 024import javax.ws.rs.core.Response; 025import javax.ws.rs.core.Response.Status.Family; 026 027import org.apache.isis.viewer.restfulobjects.applib.JsonRepresentation; 028import org.apache.isis.viewer.restfulobjects.applib.LinkRepresentation; 029import org.codehaus.jackson.JsonParseException; 030import org.codehaus.jackson.map.JsonMappingException; 031 032import com.google.common.collect.Lists; 033 034public 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}