/*
* Copyright (C) 2008-2012 Open Wide SA
*
* This library is free software; you can redistribute
* it and/or modify it under the terms of version 2.1 of
* the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
* More information at http://knowledge.openwide.fr/bin/view/Main/AlfrescoETLConnector/
*/
package fr.openwide.talendalfresco.rest.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import fr.openwide.talendalfresco.rest.RestConstants;
/**
* Base for client-side REST commands.
*
* @author Marc Dutoo - Open Wide SA
*
*/
public abstract class ClientCommandBase implements ClientCommand {
enum HttpMType {
GET,
POST
}
protected HashSet<String> defaultResultElementSet = new HashSet<String>(Arrays.asList(
new String[] { RestConstants.TAG_COMMAND, RestConstants.TAG_CODE,
RestConstants.TAG_ERROR, RestConstants.TAG_MESSAGE}));
protected HashMap<String,String> elementValueMap = new HashMap<String,String>(6);
protected String name;
private HttpMType methodType;
protected ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(3);
protected HashMap<String,NameValuePair> paramMap = new HashMap<String,NameValuePair>(3);
protected RequestEntity requestEntity = null;
protected String resultCode = null;
protected String resultError = null;
protected String resultMessage = null;
// state vars used in result parsing :
protected boolean isInCommandResponseContent = false;
protected int commandResponseContentDepth = 0;
protected StringBuffer singleLevelTextBuf = null;
public ClientCommandBase(String name, HttpMType httpMethod) {
this.name = name;
this.methodType = httpMethod;
//this.params.add("paramName", "paramValue");
//this.defaultResultElementSet.add("myelt");
}
public String getName() {
return this.name;
}
public HttpMType getMethodType() {
return this.methodType;
}
public List<NameValuePair> getParams() {
return params;
}
protected NameValuePair getParam(String paramName) {
return paramMap.get(paramName);
}
protected void setParam(NameValuePair paramValue) {
if (paramValue == null) {
return;
}
NameValuePair oldValue = paramMap.put(paramValue.getName(), paramValue);
int oldValueIndex = params.indexOf(oldValue);
oldValueIndex = (oldValueIndex == -1) ? params.size() : oldValueIndex;
params.add(oldValueIndex, paramValue);
}
/**
* Can be overriden to tune the default behaviour,
* check or build parameters...
* The client should set the params (query string) itself.
*/
public HttpMethodBase createMethod() throws RestClientException {
// instantiating a new method and configuring it
HttpMethodBase method;
switch(this.getMethodType()) {
case POST:
//case PUT:
method = new PostMethod();
break;
case GET:
default :
method = new GetMethod();
method.setFollowRedirects(true);
}
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
//method.setQueryString(params.toArray(new NameValuePair[0])); // done in client
//method.getParams().setContentCharset(this.restEncoding); // done in client
// set request entity (allow to fill method body for posts) :
if (requestEntity != null && method instanceof EntityEnclosingMethod) {
((EntityEnclosingMethod) method).setRequestEntity(requestEntity);
}
return method;
}
public String getResultCode() {
return resultCode;
}
public String getResultError() {
return resultError;
}
public String getResultMessage() {
return resultMessage;
}
/**
* Override if needed with any command specific behaviour.
* Default is same as top level.
* @param event
*/
protected void handleResponseContentEvent(XMLEvent event) {
handleResponseTopLevelEvent(event);
}
public final void handleResponse(XMLEventReader xmlReader) throws XMLStreamException {
// Read the response body and parse it
while(xmlReader.hasNext()) {
XMLEvent event = xmlReader.nextEvent();
if (isInCommandResponseContent) {
// within command content tag case
switch (event.getEventType()) {
case XMLEvent.END_ELEMENT :
commandResponseContentDepth--;
if (commandResponseContentDepth == -1) {
// means the content tag has been closed
isInCommandResponseContent = false;
}
break;
case XMLEvent.START_ELEMENT :
commandResponseContentDepth++;
}
// delegate impl of command content tag case
handleResponseContentEvent(event);
} else {
// outside command content tag case
handleResponseTopLevelEvent(event);
}
}
resultCode = elementValueMap.get(RestConstants.TAG_CODE);
resultError = elementValueMap.get(RestConstants.TAG_ERROR);
resultMessage = elementValueMap.get(RestConstants.TAG_MESSAGE);
}
protected void handleResponseTopLevelEvent(XMLEvent event) {
switch (event.getEventType()) {
case XMLEvent.CHARACTERS :
case XMLEvent.CDATA :
if (singleLevelTextBuf != null) {
singleLevelTextBuf.append(event.asCharacters().getData());
} // else element not meaningful
break;
case XMLEvent.START_ELEMENT :
StartElement startElement = event.asStartElement();
String elementName = startElement.getName().getLocalPart();
if (defaultResultElementSet.contains(elementName)) {
// reinit buffer at start of meaningful elements
singleLevelTextBuf = new StringBuffer();
} else if (RestConstants.TAG_CONTENT.equals(elementName)) {
// switch to content mode
isInCommandResponseContent = true;
} else {
singleLevelTextBuf = null; // not useful
}
break;
case XMLEvent.END_ELEMENT :
if (singleLevelTextBuf == null) {
break; // element not meaningful
}
// TODO or merely put it in the map since the element has been tested at start
EndElement endElement = event.asEndElement();
elementName = endElement.getName().getLocalPart();
if (defaultResultElementSet.contains(elementName)) {
String value = singleLevelTextBuf.toString();
elementValueMap.put(elementName, value);
// TODO test if it is code and it is not OK, break to error handling
}
// singleLevelTextBuf = new StringBuffer(); // no ! in start
break;
}
}
@SuppressWarnings("unchecked")
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("Command ");
sbuf.append(name);
sbuf.append(" with parameters ");
sbuf.append(String.valueOf(Arrays.asList(params)));
sbuf.append(" and resultCode ");
sbuf.append(resultCode);
return sbuf.toString();
}
}