package com.bluevia.commons.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import org.apache.log4j.Logger;
import com.bluevia.commons.Encoding;
import com.bluevia.commons.Entity;
import com.bluevia.commons.GsdpConstants;
import com.bluevia.commons.Utils;
import com.bluevia.commons.connector.GenericResponse;
import com.bluevia.commons.connector.IConnector;
import com.bluevia.commons.connector.http.oauth.OAuthToken;
import com.bluevia.commons.connector.http.oauth.OauthHttpConnector;
import com.bluevia.commons.data.JaxbEntity;
import com.bluevia.commons.data.StringEntity;
import com.bluevia.commons.exception.BlueviaException;
import com.bluevia.commons.exception.ConnectorException;
import com.bluevia.commons.parser.IParser;
import com.bluevia.commons.parser.ISerializer;
import com.bluevia.commons.parser.ParseException;
import com.bluevia.commons.parser.SerializeException;
import com.bluevia.commons.parser.xml.XmlParser;
import com.telefonica.schemas.unica.rest.common.v1.ClientExceptionType;
/**
* Abstract representation of an HTTP REST Client for the Bluevia API
*
* @author Telefonica R&D
*
*/
public abstract class BVBaseClient {
private static Logger log = Logger.getLogger(BVBaseClient.class.getName());
/**
* The possible values to determinate the instantiated connector for the client.
*
* Available values may be
* <ul>
* <li>LIVE: In the Live environment your application uses the real network, which means
* that you will be able to send real transactions to real Movistar,
* O2 and Vivo customers in the applicable country.</li>
* <li>TEST: The Test mode behave exactly like the Live mode, but the API calls are free of chargue, using a credits system.
* You are required to have a Movistar, O2 or Vivo mobile number to get this monthly credits.</li>
* <li>SANDBOX: The Sandbox environment offers you the exact same experience as the Live environment except
* that no traffic is generated on the live network, meaning you can experiment and play until your heart’s content.</li>
* </ul>
*
*/
public static enum Mode {LIVE, TEST, SANDBOX}
/**
* The working mode for the client
*/
protected Mode mMode;
/**
* The Connector to communicate and transmit the streams via REST
*/
protected IConnector mConnector;
/**
* The parser to parse the responses from the connector
*/
protected IParser mParser;
/**
* The serializer to serialize the requests to the connector
*/
protected ISerializer mSerializer;
/**
* The default enconding for requests used by the client
*/
protected Encoding mEncoding;
/**
* The base uri for the client
*/
protected String mBaseUri;
/**
* The Constant APPLICATION_XML.
*/
private static final String APPLICATION_XML_CONTENT_TYPE = "application/xml;charset=utf-8";
/**
* The Constant APPLICATION_JSON.
*/
private static final String APPLICATION_JSON_CONTENT_TYPE = "application/json;charset=utf-8";
/**
* The Constant APPLICATION_DEFAULT_CONTENT_TYPE.
*/
private static final String APPLICATION_DEFAULT_CONTENT_TYPE = "text/plain";
/**
* The Constant APPLICATION_URL_ENCODED.
*/
private static final String APPLICATION_URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded";
/**
* Common function to initialize trusted and untrusted common attributes:
* <ul>
* <li>Base URI</li>
* <li>Mode</li>
* </ul>
*
* @param mode
* @throws BlueviaException
*/
protected void initCommon(Mode mode) throws BlueviaException{
mBaseUri = GsdpConstants.GSDP_BASE_URL;
if (mode == null)
throw new BlueviaException("Invalid mode", BlueviaException.INVALID_MODE_EXCEPTION);
mMode = mode;
}
/**
* Function to initialize trusted clients
*
* @param mode
* @param consumerKey
* @param consumerSecret
* @param cert the path to client certificate
* @param certFilePass the password of client certificate
* @throws BlueviaException
*/
protected void initTrusted(Mode mode, String consumerKey, String consumerSecret, String cert, String certFilePass) throws BlueviaException{
initCommon(mode);
if (Utils.isEmpty(cert)) {
log.error("Empty path for keystore" );
throw new BlueviaException("Error in certificate. File path is empty or null", BlueviaException.BAD_REQUEST_EXCEPTION);
}
mConnector = new OauthHttpConnector(new OAuthToken(consumerKey, consumerSecret), cert, certFilePass);
}
/**
* Function to initialize 2-legged untrusted clients
*
* @param mode
* @param consumerKey
* @param consumerSecret
* @throws BlueviaException
*/
protected void initUntrusted(Mode mode, String consumerKey, String consumerSecret) throws BlueviaException{
initCommon(mode);
mConnector = new OauthHttpConnector(new OAuthToken(consumerKey, consumerSecret));
}
/**
* Function to initialize untrusted clients
*
* @param mode
* @param consumerKey
* @param consumerSecret
* @param token
* @param tokenSecret
* @throws BlueviaException
*/
protected void initUntrusted(Mode mode, String consumerKey, String consumerSecret, String token, String tokenSecret) throws BlueviaException{
initCommon(mode);
mConnector = new OauthHttpConnector(new OAuthToken(consumerKey, consumerSecret), new OAuthToken(token, tokenSecret));
}
/**
* Builds the uri depending on the mode used
*
* @param mode the mode
* @param uri the uri path from the API
* @return
*/
protected String buildUri(Mode mode, String uri){
switch (mode){
case LIVE:
case TEST:
return uri;
case SANDBOX:
return uri + "_Sandbox";
}
return null;
}
/**
* Creates a request using REST to the server in order to create an entity
*
* @param feedUri the REST URI of the entity to create
* @param entity the entity to create using REST into the server
* @param parameters Parameters to be added to the request
* @param headers headers of the request
* @return the response entity object
* @throws BlueviaException
* @throws IOException
* @throws SerializeException
*/
protected Entity create(String feedUri, Entity entity, HashMap<String, String> parameters, HashMap<String, String> headers)
throws BlueviaException, SerializeException, IOException {
GenericResponse response = null;
InputStream is = null;
try {
byte[] bytes = serializeEntity(entity);
String uri = mBaseUri + feedUri;
if (headers == null)
headers = new HashMap<String, String>();
headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding));
response = mConnector.create(uri, parameters, bytes, headers);
is = response.getAdditionalData().getBody();
return parseEntity(is);
} catch (ConnectorException e){
if (e.getAdditionalData().getBody() == null)
throw e;
else {
is = e.getAdditionalData().getBody();
String error = parseError(is);
String completeError = e.getMessage();
if (error != null)
completeError += ": " + error;
throw new ConnectorException(completeError, e.getCode());
}
} finally {
closeStream(is);
}
}
/**
* Creates a request using REST to the server in order to retrieve an entity
* from the server
*
* @param feedUri the REST URI of the entity to get
* @return the response entity object
* @throws BlueviaException
* @throws IOException
*/
protected Entity retrieve(String feedUri) throws BlueviaException, IOException {
return retrieve(feedUri, null);
}
/**
* Creates a request using REST to the server in order to retrieve an entity
* from the server
*
* @param feedUri the REST URI of the entity to get
* @param parameters the parameters to be added to the URI
* @return the response entity object
* @throws BlueviaException
* @throws IOException
*/
protected Entity retrieve(String feedUri, HashMap<String, String> parameters) throws BlueviaException, IOException {
return retrieve(feedUri, parameters, null);
}
/**
* Creates a request using REST to the server in order to retrieve an entity
* from the server
*
* @param feedUri the REST URI of the entity to get
* @param parameters the parameters to be added to the URI
* @param headers headers of the request
* @return the response entity object
* @throws BlueviaException
* @throws IOException
*/
protected Entity retrieve(String feedUri, HashMap<String, String> parameters, HashMap<String, String> headers) throws BlueviaException, IOException {
GenericResponse response = null;
InputStream is = null;
try {
String uri = mBaseUri + feedUri;
if (headers == null)
headers = new HashMap<String, String>();
headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding));
response = mConnector.retrieve(uri, parameters);
is = response.getAdditionalData().getBody();
return parseEntity(is);
} catch (ConnectorException e){
if (e.getAdditionalData().getBody() == null)
throw e;
else {
is = e.getAdditionalData().getBody();
String error = parseError(is);
String completeError = e.getMessage();
if (error != null)
completeError += ": " + error;
throw new ConnectorException(completeError, e.getCode());
}
} finally {
closeStream(is);
}
}
/**
* Creates a request using REST to the server in order to create an entity
*
* @param feedUri the REST URI of the entity to create
* @param entity the entity to create using REST into the server
* @param parameters Parameters to be added to the request
* @param headers headers of the request
* @return the response entity object
* @throws BlueviaException
* @throws IOException
*/
protected Entity update(String feedUri, Entity entity, HashMap<String, String> parameters, HashMap<String, String> headers) throws BlueviaException, IOException {
GenericResponse response = null;
InputStream is = null;
try {
byte[] bytes = serializeEntity(entity);
String uri = mBaseUri + feedUri;
if (headers == null)
headers = new HashMap<String, String>();
headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding));
response = mConnector.update(uri, parameters, bytes, headers);
is = response.getAdditionalData().getBody();
return parseEntity(is);
} catch (ConnectorException e){
if (e.getAdditionalData().getBody() == null)
throw e;
else {
is = e.getAdditionalData().getBody();
String error = parseError(is);
String completeError = e.getMessage();
if (error != null)
completeError += ": " + error;
throw new ConnectorException(completeError, e.getCode());
}
} finally {
closeStream(is);
}
}
/**
* Creates a request using REST to the server in order to retrieve an entity
* from the server
*
* @param feedUri the REST URI of the entity to get
* @return the response entity object
* @throws BlueviaException
* @throws IOException
*/
protected Entity delete(String feedUri) throws BlueviaException, IOException {
return delete(feedUri, null);
}
/**
* Creates a request using REST to the server in order to retrieve an entity
* from the server
*
* @param feedUri the REST URI of the entity to get
* @param parameters the parameters to be added to the URI
* @return the response entity object
* @throws BlueviaException
* @throws IOException
*/
protected Entity delete(String feedUri, HashMap<String, String> parameters) throws BlueviaException, IOException {
return delete(feedUri, parameters, null);
}
/**
* Creates a request using REST to the server in order to retrieve an entity
* from the server
*
* @param feedUri the REST URI of the entity to get
* @param parameters the parameters to be added to the URI
* @param headers headers of the request
* @return the response entity object
* @throws BlueviaException
* @throws IOException
*/
protected Entity delete(String feedUri, HashMap<String, String> parameters, HashMap<String, String> headers) throws BlueviaException, IOException {
GenericResponse response = null;
InputStream is = null;
try {
String uri = mBaseUri + feedUri;
if (headers == null)
headers = new HashMap<String, String>();
headers.put(GsdpConstants.HEADER_CONTENT_TYPE, getEncoding(mEncoding));
response = mConnector.delete(uri, parameters, headers);
is = response.getAdditionalData().getBody();
return parseEntity(is);
} catch (ConnectorException e){
if (e.getAdditionalData().getBody() == null)
throw e;
else {
is = e.getAdditionalData().getBody();
String error = parseError(is);
String completeError = e.getMessage();
if (error != null)
completeError += ": " + error;
throw new ConnectorException(completeError, e.getCode());
}
} finally {
closeStream(is);
}
}
/**
* Closes the client and frees all the resources it might have used.
*/
public void close() {
mConnector.close();
}
/**
* Serializes an Entity
*
* @param entity the entity to serialize
* @return the contents of the Entity in a byte array
* @throws SerializeException
*/
protected byte[] serializeEntity(Entity entity) throws SerializeException {
if (mSerializer == null)
return null;
ByteArrayOutputStream out = null;
try {
out = mSerializer.serialize(entity);
byte[] entryBytes = out.toByteArray();
out.close();
if (entryBytes != null) {
try {
log.debug("Serialized entry: " + new String(entryBytes, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// should not happen
throw new IllegalStateException("UTF-8 should be supported!", e);
}
}
return entryBytes;
} catch (IOException e) {
log.info( e.getMessage(), e);
} finally {
if (out != null)
try {
out.close();
} catch (IOException e) {
log.info( e.getMessage(), e);
}
}
return null;
}
/**
* Parses the response into an Entity
*
* @param is the InputStream containing the stream with the entity information
* @return the Entity object
* @throws ParseException when an error occurs converting the stream into an object
*/
protected Entity parseEntity(InputStream is) throws ParseException {
log.debug("Start parseEntity");
if (mParser == null)
return null;
try {
return mParser.parse(is);
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
log.info( e.getMessage(), e);
}
}
}
/**
* Parses the error message
*
* @param is the InputStream containing the stream with the entity information
* @return the Entity object
* @throws ParseException when an error occurs converting the stream into an object
*/
protected String parseError(InputStream is) throws ParseException {
log.debug("Start parseError");
if (mParser == null)
return null;
try {
((XmlParser) mParser).setParseClass(ClientExceptionType.class);
Entity entity = mParser.parse(is);
//if ((entity == null) || (! (entity instanceof JaxbEntity)))
//throw new BlueviaException("Error during request. Response received does not correspond to an DeliveryInfo",
// BlueviaException.INTERNAL_CLIENT_ERROR);
JaxbEntity parseEntity= (JaxbEntity) entity;
if (parseEntity.getObject() instanceof ClientExceptionType) {
ClientExceptionType cet= (ClientExceptionType) parseEntity.getObject();
StringEntity error = new StringEntity(cet.getText());
return error.getText();
}
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
log.info( e.getMessage(), e);
}
}
return null;
}
/**
* Closes an input stream
*
* @param is
*/
protected void closeStream(InputStream is){
if (is != null)
try {
is.close();
} catch (IOException e) {
log.info( e.getMessage(), e);
}
}
/**
* Translates the Encoding enum into the string representation for the request
*
* @param encoding
* @return the string representation of the Encoding enum
*/
protected String getEncoding(Encoding encoding){
switch (encoding) {
case APPLICATION_XML:
return APPLICATION_XML_CONTENT_TYPE;
case APPLICATION_JSON:
return APPLICATION_JSON_CONTENT_TYPE;
case APPLICATION_URL_ENCODED:
return APPLICATION_URL_ENCODED_CONTENT_TYPE;
default:
return APPLICATION_DEFAULT_CONTENT_TYPE;
}
}
}