/* * Copyright (c) 2014. by Robusta Code and individual contributors * as indicated by the @authors tag. See the copyright.txt in the * distribution for a full listing of individual contributors. * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package io.robusta.rra.client; import io.robusta.rra.exception.HttpException; import io.robusta.rra.exception.RestException; import io.robusta.rra.representation.Representation; import io.robusta.rra.utils.Couple; import io.robusta.rra.utils.CoupleList; import io.robusta.rra.utils.StringUtils; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** * This class is used both for Synchronous and Asynchronous client * <p/> * Created by Nicolas Zozol for Robusta Code * * @author Nicolas Zozol */ public abstract class AbstractRestClient<Client> implements RestClient<Client> { /** * can be easily configured outside */ protected static String applicationUri; protected static String defaultContentType = RestClient.xmlContentType; /** * */ protected static String authorizationValue = ""; protected Map<String, String> responseHeaders = new HashMap<String, String>(); /** * */ protected String entity = ""; /** * */ protected String credentials = ""; /** * */ protected int httpCode; /** * */ protected String response; /** * */ protected String contentType = RestClient.jsonContentType; /** * set the default content type of the request * @param contentType */ public static void setDefaultContentType( String contentType ) { defaultContentType = contentType; if ( contentType.equals( "xml" ) ) { defaultContentType = RestClient.xmlContentType; } if ( contentType.equals( "html" ) ) { defaultContentType = RestClient.htmlContentType; } if ( contentType.equals( "form" ) ) { defaultContentType = RestClient.formContentType; } if ( contentType.equals( "json" ) ) { defaultContentType = RestClient.jsonContentType; } } /** * For non-gwt application, it will check that uri starts with http or https * * @param applicationUri * @throws IllegalArgumentException * if uri does not start with http:// or https:// */ protected void checkConstructorUri( String applicationUri ) throws IllegalArgumentException { if ( !applicationUri.startsWith( "http://" ) && !applicationUri.startsWith( "https://" ) ) { throw new IllegalArgumentException( "applicationUri must start with http:// or https://" ); } } /** * Returns the full uri and the request Body in a String[2] array If content * is Form, and Method is POST or PUT, parameters will go in the entity For * all other cases, it will go to the url, such as ?param1=Jo¶m2=Bob * * @param method * @param relativePath * @param parameters * @param representation * @return result[0] is the full URI, result[1] is the entity * @throws io.robusta.rra.exception.HttpException */ public String[] prepareMethod( HttpMethod method, String relativePath, CoupleList<String, Object> parameters, Representation representation ) throws HttpException { this.responseHeaders = new HashMap<String, String>(); String body = entity; // Making a clear uri String url = StringUtils.addPath( applicationUri, relativePath ); // Dealing with parameters // Special case of forms && POST/PUT methods if ( representation != null ) { // params will go in the entity // adding parameters to the entity assert entity == null || entity.length() == 0; // body = encodeFormEntity(parameters); body = representation.toString(); } else if ( ( parameters != null && parameters.size() > 0 ) ) { url = encodeUrl( applicationUri, relativePath, parameters ); } return new String[] { url, body }; } /** * Set autorizationValue as a static method * * @param authorizationValue */ public void setAuthorizationValue( String authorizationValue ) { AbstractRestClient.authorizationValue = authorizationValue; } /** * check if the content type is a form * @return */ protected boolean contentTypeIsForm() { return this.contentType.contains( "x-www-form-urlencoded" ); } /** * Once the contentType is set, it will be used as long as the object lives. * default is "application/x-www-form-urlencoded;charset=utf-8". If you * contentType="" or "form", it will be set to default. If you just put * contentType="xml", it will be "application/xml;charset=utf-8" * * @param contentType */ public void setContentType( String contentType ) { this.contentType = contentType; if ( contentType.equals( "xml" ) ) { this.contentType = RestClient.xmlContentType; } if ( contentType.equals( "html" ) ) { this.contentType = RestClient.htmlContentType; } if ( contentType.equals( "form" ) ) { this.contentType = RestClient.formContentType; } if ( contentType.equals( "json" ) ) { this.contentType = RestClient.jsonContentType; } } /** * May be done only once, as the application path is static * * @param applicationUri */ public void setApplicationUri( String applicationUri ) { AbstractRestClient.applicationUri = applicationUri; } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#setNextEntity(java.lang.String) */ public void setNextEntity( String postBody ) { this.entity = postBody; } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#getHttpCode() */ public int getHttpCode() { return httpCode; } // TODO /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#getResponse() */ public String getResponse() { return response; } /** * reset the request */ public void clean() { setNextEntity( "" ); this.contentType = defaultContentType; } /** * Encode an URL parameter with UTF-8, as asked by wwc Exemple : * encodeParameter("A B C $%" returns "A+B+C %24%25" * * @param nameOrValue * @return */ public abstract String encodeParameter( String nameOrValue ); /** * build url http compliant * @param applicationUri is the base Uri of the application * @param relativePath is the relative path of the request * @param parameters are the parameters of the request * @return the encode url */ protected String encodeUrl( String applicationUri, String relativePath, CoupleList<String, Object> parameters ) { String url = StringUtils.addPath( applicationUri, relativePath ); StringBuilder result = new StringBuilder( url ); // throw new // HttpException("Can't do a POST method with both parameters and postbody"); if ( !parameters.isEmpty() ) { result.append( "?" ); } for ( int i = 0; i < parameters.size(); i++ ) { Couple<String, Object> c = parameters.get( i ); String paramName = encodeParameter( c.getLeft() ); String paramValue = encodeParameter( c.getRight().toString() ); result.append( paramName ); result.append( "=" ); result.append( paramValue ); // StringUtilities.replaceAll(value, "+","%20"); if ( i != parameters.size() - 1 ) { result.append( "&" ); } } return result.toString(); } /** * build the form entity * @param parameters * @return the encoded form entity */ protected String encodeFormEntity( CoupleList<String, Object> parameters ) { StringBuilder result = new StringBuilder(); boolean firstLine = true; for ( int i = 0; i < parameters.size(); i++ ) { if ( firstLine ) { firstLine = false; } else { result.append( "\n" ); } Couple<String, Object> c = parameters.get( i ); String paramName = encodeParameter( c.getLeft() ); String paramValue = encodeParameter( c.getRight().toString() ); result.append( paramName ); result.append( "=" ); result.append( paramValue ); } return result.toString(); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#getHeaderResponse() */ @Override public Map<String, String> getHeaderResponse() { return this.responseHeaders; } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#POST(java.lang.String, * io.robusta.rra.representation.Representation) */ @Override public String POST( String relativeFileWithNoParam, Representation representation ) throws HttpException { String[] obj = prepareMethod( HttpMethod.POST, relativeFileWithNoParam, null, representation ); assert obj.length == 2; return executeMethod( HttpMethod.POST, obj[0], obj[1] ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#GET(java.lang.String, * io.robusta.rra.utils.CoupleList) */ @Override public String GET( String relativeFileWithNoParam, CoupleList<String, Object> parameters ) throws HttpException { String[] obj = prepareMethod( HttpMethod.GET, relativeFileWithNoParam, parameters, null ); assert obj.length == 2; return executeMethod( HttpMethod.GET, obj[0], obj[1] ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#PUT(java.lang.String, * io.robusta.rra.representation.Representation) */ @Override public String PUT( String relativeFileWithNoParam, Representation representation ) throws HttpException { String[] obj = prepareMethod( HttpMethod.PUT, relativeFileWithNoParam, null, representation ); assert obj.length == 2; return executeMethod( HttpMethod.PUT, obj[0], obj[1] ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#DELETE(java.lang.String, * io.robusta.rra.utils.CoupleList) */ @Override public String DELETE( String relativeFileWithNoParam, CoupleList<String, Object> parameters ) throws HttpException { String[] obj = prepareMethod( HttpMethod.DELETE, relativeFileWithNoParam, parameters, null ); assert obj.length == 2; return executeMethod( HttpMethod.DELETE, obj[0], obj[1] ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#OTHER(java.lang.String, * java.lang.String, io.robusta.rra.utils.CoupleList) */ @Override public String OTHER( String method, String relativeFileWithNoParam, CoupleList<String, Object> parameters ) throws HttpException { String[] obj = prepareMethod( HttpMethod.OTHER.setMethod( method ), relativeFileWithNoParam, parameters, null ); assert obj.length == 2; return executeMethod( HttpMethod.OTHER.setMethod( method ), obj[0], obj[1] ); } /** * This method has the responsability do make the correct call with a * specific implementation * * @param method * @param url * @param entity * @return * @throws HttpException */ protected abstract String executeMethod( final HttpMethod method, final String url, final String entity ) throws HttpException; /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#get(java.lang.String, * io.robusta.rra.utils.CoupleList, io.robusta.rra.client.Callback) */ @Override public void get( String relativePath, CoupleList<String, Object> parameters, Callback callback ) throws HttpException { String[] obj = prepareMethod( HttpMethod.GET, relativePath, parameters, null ); assert obj.length == 2; String url = obj[0], body = obj[1]; executeMethod( HttpMethod.GET, url, body, callback ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#post(java.lang.String, * io.robusta.rra.representation.Representation, * io.robusta.rra.client.Callback) */ @Override public void post( String relativePath, Representation representation, Callback callback ) throws HttpException { String[] obj = prepareMethod( HttpMethod.POST, relativePath, null, representation ); assert obj.length == 2; String url = obj[0], body = obj[1]; setNextEntity( body ); executeMethod( HttpMethod.POST, url, body, callback ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#put(java.lang.String, * io.robusta.rra.representation.Representation, * io.robusta.rra.client.Callback) */ @Override public void put( String relativePath, Representation representation, Callback callback ) throws HttpException { String[] obj = prepareMethod( HttpMethod.PUT, relativePath, null, representation ); assert obj.length == 2; String url = obj[0], body = obj[1]; setNextEntity( body ); executeMethod( HttpMethod.PUT, url, body, callback ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#delete(java.lang.String, * io.robusta.rra.utils.CoupleList, io.robusta.rra.client.Callback) */ @Override public void delete( String relativePath, CoupleList<String, Object> parameters, Callback callback ) throws HttpException { String[] obj = prepareMethod( HttpMethod.DELETE, relativePath, parameters, null ); assert obj.length == 2; String url = obj[0], body = obj[1]; setNextEntity( body ); executeMethod( HttpMethod.DELETE, url, body, callback ); } /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#other(java.lang.String, * java.lang.String, io.robusta.rra.utils.CoupleList, * io.robusta.rra.client.Callback) */ @Override public void other( String method, String relativePath, CoupleList<String, Object> parameters, Callback callback ) throws HttpException { String[] obj = prepareMethod( HttpMethod.OTHER.setMethod( method ), relativePath, parameters, null ); assert obj.length == 2; String url = obj[0], body = obj[1]; setNextEntity( body ); executeMethod( HttpMethod.OTHER.setMethod( method ), url, body, callback ); } // TODO : add the method getResponse /* * (non-Javadoc) * * @see io.robusta.rra.client.RestClient#join() */ @Override public abstract void join(); /** * This method has the responsability do make the correct call with a * specific implementation for an asynchronous call * @param method * @param url * @param entity * @param callback for an asynchronous call * @throws HttpException */ protected abstract void executeMethod( final HttpMethod method, final String url, final String entity, final Callback callback ) throws HttpException; /** * calls the callback succes or failure, using the httpcode * * @param httpCode * @param inputStream * @param callback */ protected void callCallback( Callback callback, int httpCode, InputStream inputStream ) { if ( httpCode >= 200 && httpCode < 300 ) { callback.onSuccess( inputStream ); callback.onComplete(); } else if ( httpCode >= 300 && httpCode < 400 ) { // no success } else if ( httpCode >= 400 && httpCode < 600 ) { } else if ( httpCode < 600 ) { callback.onFailure( new RestException( httpCode, inputStream ) ); } } }