CMSRequest.java (11259B)
1 /* 2 OpenRat Java-Client 3 Copyright (C) 2009 Jan Dankert 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with this library; if not, write to the 17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 20 */ 21 package de.openrat.client.util; 22 23 import java.io.IOException; 24 import java.io.PrintWriter; 25 import java.io.StringReader; 26 import java.util.*; 27 28 import javax.xml.parsers.DocumentBuilder; 29 import javax.xml.parsers.DocumentBuilderFactory; 30 import javax.xml.parsers.ParserConfigurationException; 31 32 import de.openrat.client.Version; 33 import org.w3c.dom.Document; 34 import org.w3c.dom.Node; 35 import org.w3c.dom.NodeList; 36 import org.xml.sax.InputSource; 37 import org.xml.sax.SAXException; 38 39 import de.openrat.client.CMSClient; 40 import de.openrat.client.util.CMSNotice.CMSErrorStatus; 41 import de.openrat.client.util.HttpRequest.HttpMethod; 42 43 /** 44 * API-Request to the OpenRat Content Management System. <br> 45 * <br> 46 * The call to the CMS server is done via a (non-SSL) HTTP connection.<br> 47 * <br> 48 * Before a call you are able to set some key/value-pairs as parameters. After 49 * calling the CMS a DOM-document is returned, which contains the server 50 * response.<br> 51 * Example <br> 52 * 53 * <pre> 54 * CMSRequest request = new CMSRequest("your.openrat.example.com"); 55 * // prints tracing information to stdout. 56 * request.trace = true; 57 * try 58 * { 59 * request.parameter.put("action", "index"); 60 * request.parameter.put("subaction", "showlogin"); // login page 61 * request.parameter.put("...", "..."); 62 * Document response = request.call(); 63 * // now traverse through the dom tree and get your information. 64 * } 65 * catch (IOException e) 66 * { 67 * // your error handling. 68 * } 69 * </pre> 70 * 71 * @author Jan Dankert 72 */ 73 public class CMSRequest { 74 75 private String actionMethod; 76 private String action; 77 private ParameterMap parameter = new ParameterMap(); 78 79 private PrintWriter logWriter; 80 81 private HttpMethod method = HttpMethod.GET; 82 83 private CMSConnection connection; 84 85 /** 86 * Marks this request for writing. 87 */ 88 public CMSRequest forWriting() { 89 this.method = HttpMethod.POST; 90 return this; 91 } 92 93 /** 94 */ 95 public CMSRequest forReading() { 96 this.method = HttpMethod.GET; 97 return this; 98 } 99 100 /** 101 * Constructs a CMS-Request to the specified server/path/port. 102 * 103 * @param connection Connection to server 104 */ 105 public CMSRequest(CMSConnection connection) { 106 super(); 107 this.connection = connection; 108 this.logWriter = connection.getLogWriter(); 109 } 110 111 112 public CMSRequest(CMSConnection connection, String action, String method) { 113 this(connection); 114 this.action = action; 115 this.actionMethod = method; 116 } 117 118 119 public CMSRequest addParameter(String name, CharSequence value) { 120 this.parameter.put(name, value.toString()); 121 return this; 122 } 123 124 public CMSRequest addParameter(String name, long value) { 125 this.parameter.put(name, Long.toString(value)); 126 return this; 127 } 128 129 130 /** 131 * Sends a request to the openrat-server and parses the response into a DOM 132 * tree document. 133 * 134 * @return server response as a DOM tree 135 * @throws IOException if server is unrechable or responds non-wellformed XML 136 */ 137 public CMSResponse execute() throws IOException { 138 139 parameter.put("action",this.action); 140 parameter.put("subaction",this.actionMethod); 141 parameter.put("token", connection.getToken()); 142 143 HttpRequest httpRequest = new HttpRequest(); 144 httpRequest.setMethod(method); 145 146 httpRequest.getRequestHeader().put("User-Agent", "Mozilla/5.0; compatible (OpenRat CMS java-client)"); 147 httpRequest.getRequestHeader().put("Accept", "application/xml"); 148 httpRequest.getRequestHeader().put("Accept-Charset", "utf-8"); 149 150 final HttpClient httpClient = new HttpClient(connection); 151 httpClient.getParameter().putAll(parameter); 152 153 HttpResponse httpResponse = httpClient.execute(httpRequest); 154 155 final CMSNode rootNode; 156 try { 157 // Try XML parsing 158 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 159 final DocumentBuilder builder = factory.newDocumentBuilder(); 160 final Document document = builder.parse(new InputSource(new StringReader(httpResponse.getPayload()))); 161 rootNode = convertXMLNodeIntoCMSNode(document.getDocumentElement()); 162 } catch (ParserConfigurationException e) { 163 if (logWriter != null) { 164 e.printStackTrace(logWriter); 165 } 166 throw new CMSException("XML-Parser-Configuration invalid: " + e.getMessage(), e); 167 } catch (SAXException e) { 168 throw new CMSServerErrorException("Server did not return a valid XML-document: " + httpResponse.getPayload(), "" 169 + httpResponse.getHttpStatus().getStatusCode(), httpResponse.getHttpStatus().getServerMessage(), e.getMessage(), e); 170 } 171 172 CMSResponse cmsResponse = createCMSResponse(httpResponse.getHttpStatus(), rootNode); 173 174 cmsResponse.setHttpStatus(httpResponse.getHttpStatus().getStatusCode()); 175 connection.setToken(cmsResponse.getSession().getToken()); 176 return cmsResponse; 177 } 178 179 /** 180 * Erzeugt aus 181 * 182 * @param httpStatus 183 * @param rootNode 184 * @return 185 */ 186 private CMSResponse createCMSResponse(HttpStatus httpStatus, final CMSNode rootNode) { 187 188 if (httpStatus.getStatusCode() == 204) { 189 return null; // No content 190 } 191 192 if (httpStatus.isServerError()) { 193 if (rootNode.getName().equals("server") || rootNode.getName().equals("error")) { 194 // Server reports an technical error. 195 String error = rootNode.getFirstChildByName("error").getValue(); 196 String status = rootNode.getFirstChildByName("status").getValue(); 197 String description = rootNode.getFirstChildByName("description").getValue(); 198 String reason = rootNode.getFirstChildByName("reason").getValue(); 199 200 ServerSideException cause = null; 201 202 if (rootNode.getFirstChildByName("trace") != null) 203 cause = createExceptionFromServerTrace(rootNode.getFirstChildByName("trace")); 204 205 throw new CMSServerErrorException(error, status, description, reason, cause); 206 207 } else { 208 throw new CMSServerErrorException(httpStatus.getServerMessage(), "" + httpStatus.getStatusCode(), "", ""); 209 } 210 211 } 212 213 if (httpStatus.getStatusCode() == 200) { 214 215 if (rootNode.getName() == "server") { 216 // Server reports an answer 217 CMSResponse cmsResponse = createCMSReponse(rootNode); 218 219 return cmsResponse; 220 } else { 221 // HTTP-Status 200 OK, but no XML-Element "server" found. 222 throw new CMSServerErrorException(httpStatus.getServerMessage(), "" + httpStatus.getStatusCode(), "", "no SERVER element found"); 223 } 224 } else { 225 // Unknown HTTP Status 226 throw new CMSServerErrorException(httpStatus.getServerMessage(), "" + httpStatus.getStatusCode(), "", "Unsupported HTTP Status"); 227 } 228 } 229 230 private ServerSideException createExceptionFromServerTrace(CMSNode trace) { 231 232 final List<StackTraceElement> traceElements = new ArrayList<>(); 233 234 for (CMSNode traceElementNode : trace.getChildren()) { 235 236 String file = traceElementNode.getFirstChildValue("file"); 237 String line = traceElementNode.getFirstChildValue("line"); 238 239 int lineNumber = 0; 240 if (line != null) 241 lineNumber = Integer.parseInt(line); 242 243 String fct = traceElementNode.getFirstChildValue("function"); 244 String cls = traceElementNode.getFirstChildValue("class"); 245 if (cls == null) cls = ""; 246 traceElements.add(new StackTraceElement(cls, fct, file, lineNumber)); 247 } 248 249 String name = "Exception"; 250 String message = "server error"; 251 ServerSideException cause = new ServerSideException(message, name); 252 253 cause.setStackTrace(traceElements.toArray(new StackTraceElement[]{})); 254 255 return cause; 256 } 257 258 private CMSResponse createCMSReponse(final CMSNode rootNode) { 259 260 CMSResponse cmsResponse = new CMSResponse(); 261 262 // Do we support the server api version? 263 Version apiVersion = new Version(rootNode.getFirstChildByName("api").getValue()); 264 265 if (!apiVersion.equals(CMSClient.SUPPORTED_API_VERSION)) { 266 // oh no, the server api is older or newer than our client api. 267 // there is nothing we can do. 268 throw new CMSServerErrorException("Only API Version " + CMSClient.SUPPORTED_API_VERSION + 269 " is supported. The server is using API Version " + apiVersion); 270 } 271 272 cmsResponse.setApi(apiVersion); 273 cmsResponse.setVersion(new Version(rootNode.getFirstChildByName("version").getValue())); 274 275 List<String> errorList = new ArrayList<String>(); 276 for (CMSNode errorNode : rootNode.getFirstChildByName("errors").getChildren()) { 277 errorList.add(errorNode.getValue()); 278 } 279 cmsResponse.setValidationErrors(errorList); 280 281 List<CMSNotice> noticeList = new ArrayList<CMSNotice>(); 282 283 for (CMSNode noticeNode : rootNode.getFirstChildByName("notices").getChildren()) { 284 CMSNotice error = new CMSNotice(); 285 error.setKey(noticeNode.getFirstChildByName("key").getValue()); 286 error.setType(noticeNode.getFirstChildByName("type").getValue()); 287 error.setName(noticeNode.getFirstChildByName("name").getValue()); 288 error.setText(noticeNode.getFirstChildByName("text").getValue()); 289 290 String status = noticeNode.getFirstChildByName("status").getValue(); 291 292 if (status.equalsIgnoreCase("ok")) 293 error.setStatus(CMSErrorStatus.NOTICE); 294 else if (status.equalsIgnoreCase("warning")) 295 error.setStatus(CMSErrorStatus.WARN); 296 else if (status.equalsIgnoreCase("info")) 297 error.setStatus(CMSErrorStatus.INFO); 298 else 299 error.setStatus(CMSErrorStatus.ERROR); 300 noticeList.add(error); 301 } 302 cmsResponse.setNotices(noticeList); 303 304 CMSNode sessionNode = rootNode.getFirstChildByName("session"); 305 CMSSession session = new CMSSession(); 306 session.setName(sessionNode.getFirstChildByName("name").getValue()); 307 session.setId(sessionNode.getFirstChildByName("id").getValue()); 308 session.setToken(sessionNode.getFirstChildByName("token").getValue()); 309 cmsResponse.setSession(session); 310 311 cmsResponse.setOutput(rootNode.getFirstChildByName("output")); 312 return cmsResponse; 313 } 314 315 public static Iterable<Node> iterable(final NodeList n) { 316 317 return new Iterable<Node>() { 318 319 public Iterator<Node> iterator() { 320 321 return new Iterator<Node>() { 322 323 int index = 0; 324 325 public boolean hasNext() { 326 return index < n.getLength(); 327 } 328 329 public Node next() { 330 if (hasNext()) { 331 return n.item(index++); 332 } else { 333 throw new NoSuchElementException(); 334 } 335 } 336 337 public void remove() { 338 throw new UnsupportedOperationException(); 339 } 340 }; 341 } 342 }; 343 } 344 345 private static CMSNode convertXMLNodeIntoCMSNode(Node node) { 346 347 List<CMSNode> children = new ArrayList<CMSNode>(); 348 349 for (Node nodex : iterable(node.getChildNodes())) { 350 if (nodex.getNodeType() == Node.ELEMENT_NODE) { 351 352 CMSNode childNode = convertXMLNodeIntoCMSNode(nodex); 353 children.add(childNode); 354 } 355 } 356 357 return new CMSNode(node.getNodeName(), node.getTextContent(), children); 358 } 359 }