openrat-java-client

Unnamed repository; edit this file 'description' to name the repository.
git clone http://git.code.weiherhei.de/openrat-java-client.git
Log | Files | Refs

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(&quot;your.openrat.example.com&quot;);
     55  * // prints tracing information to stdout.
     56  * request.trace = true;
     57  * try
     58  * {
     59  * 	request.parameter.put(&quot;action&quot;, &quot;index&quot;);
     60  * 	request.parameter.put(&quot;subaction&quot;, &quot;showlogin&quot;); // login page
     61  * 	request.parameter.put(&quot;...&quot;, &quot;...&quot;);
     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 }