All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.unicore.services.restclient.BaseClient Maven / Gradle / Ivy

The newest version!
package eu.unicore.services.restclient;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpMessage;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.logging.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;

import eu.unicore.security.wsutil.SecuritySessionUtils;
import eu.unicore.util.Log;
import eu.unicore.util.httpclient.HttpUtils;
import eu.unicore.util.httpclient.IClientConfiguration;
import eu.unicore.util.httpclient.SessionIDProvider;
import eu.unicore.util.httpclient.SessionIDProviderImpl;

/**
 * Basic client for working with REST, JSON and all that
 * 
 * An instance of this client is intended to work with a single UNICORE server.
 * It automatically handles security session IDs and user preferences 
* * NOT THREADSAFE * * @author schuller */ public class BaseClient { protected static final Logger logger = Log.getLogger(Log.CLIENT, BaseClient.class); protected final HttpClient client; protected StatusLine status; protected IAuthCallback authCallback; protected SessionIDProvider sessionIDProvider; protected IClientConfiguration security; protected boolean useSessions; protected String url; private final DequeurlStack = new ArrayDeque<>(); protected UserPreferences userPreferences = new UserPreferences(); protected boolean errorChecking = true; public BaseClient(String url, IClientConfiguration security){ this(url,security,null); } public BaseClient(String url, IClientConfiguration security, IAuthCallback authCallback){ HttpClient client = HttpUtils.createClient(url, security); this.client = client; this.authCallback = authCallback; this.sessionIDProvider = security.getSessionIDProvider(); if(sessionIDProvider==null){ sessionIDProvider = new SessionIDProviderImpl(); } this.security = security; this.useSessions = security.useSecuritySessions(); this.url = url; setupUserPreferences(); } protected void setupUserPreferences() { try { Map userPrefs = security.getRequestedUserAttributes(); if(userPrefs.get("role")!=null) { userPreferences.setRole(userPrefs.get("role")[0]); } if(userPrefs.get("uid")!=null) { userPreferences.setUid(userPrefs.get("uid")[0]); } if(userPrefs.get("group")!=null) { userPreferences.setGroup(userPrefs.get("group")[0]); } if(userPrefs.get("supplementaryGroups")!=null) { userPreferences.setSupplementaryGroups(userPrefs.get("supplementaryGroups")); } }catch(Exception ex) { Log.logException("Cannot configure user preferences.", ex); } } /** * set the URL of the resource to access - can be reverted to the previous state using pop() */ public void pushURL(String url){ urlStack.push(this.url); this.url = url; } /** * revert to the previous URL */ public void popURL() { this.url = urlStack.pop(); } /** * set the URL of the resource to access (also clears url "history") */ public void setURL(String url){ this.url = url; urlStack.clear(); } public String getURL(){ return url; } public UserPreferences getUserPreferences() { return userPreferences; } public IAuthCallback getAuthCallback() { return authCallback; } public IClientConfiguration getSecurityConfiguration() { return security; } public void setAuthCallback(IAuthCallback authCallback){ this.authCallback = authCallback; } protected void addAuth(HttpMessage message) throws Exception { if(authCallback!=null){ authCallback.addAuthenticationHeaders(message); } } protected void addUserPreferences(HttpMessage message) throws Exception { if(userPreferences!=null){ userPreferences.addUserPreferencesHeader(message); } } /** * get the JSON representation of this resource * * @return {@link JSONObject} * @throws Exception */ public JSONObject getJSON() throws Exception { return getJSON(ContentType.APPLICATION_JSON); } /** * get the JSON representation of this resource * * @param accept - the MediaType to use, if null, "application/json" is used * @throws Exception */ public JSONObject getJSON(ContentType accept) throws Exception { ClassicHttpResponse response = get(accept==null ? ContentType.APPLICATION_JSON : accept); return asJSON(response); } public ClassicHttpResponse get(ContentType accept) throws Exception { return get(accept, null); } /** * * @param accept * @param headers - custom headers (can be null) */ public ClassicHttpResponse get(ContentType accept, Mapheaders) throws Exception { HttpGet get=new HttpGet(url); if(accept!=null)get.setHeader("Accept", accept.getMimeType()); if(headers!=null){ for(Map.Entry header: headers.entrySet()){ get.setHeader(header.getKey(), header.getValue()); } } return execute(get); } /** * put content to this resource, returning the response. The caller must deal with * the response to avoid resource leaks and blocked connections! * * @param content * @param type * @return HttpResponse * @throws Exception */ public ClassicHttpResponse put(InputStream content, ContentType type) throws Exception { HttpPut put=new HttpPut(url); put.setEntity(new InputStreamEntity(content, type)); return execute(put); } /** * put content to this resource, discarding any response */ public void putQuietly(InputStream content, ContentType type) throws Exception { HttpPut put=new HttpPut(url); put.setEntity(new InputStreamEntity(content, type)); try(ClassicHttpResponse response = execute(put)){ EntityUtils.consumeQuietly(response.getEntity()); } } /** * PUT the JSON to this resource * * @return {@link JSONObject} * @throws Exception */ public ClassicHttpResponse put(JSONObject content) throws Exception { HttpPut put=new HttpPut(url); put.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()); put.setEntity(new StringEntity(content.toString(), ContentType.APPLICATION_JSON)); return execute(put); } /** * PUT the JSON to this resource, discard the response */ public void putQuietly(JSONObject content) throws Exception { try(ClassicHttpResponse response = put(content)){ checkError(response); EntityUtils.consumeQuietly(response.getEntity()); } } /** * create a new resource by POSTing the JSON to this resource. * Returns the value of the "Location" header which is * the URL of the newly created resource. * @return URL of the newly created resource or null if not available */ public String create(JSONObject content) throws Exception { HttpPost post=new HttpPost(url); post.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()); if(content!=null)post.setEntity(new StringEntity(content.toString(), ContentType.APPLICATION_JSON)); try(ClassicHttpResponse response = execute(post)){ Header l = response.getFirstHeader("Location"); return l!=null? l.getValue():null; } } /** * post the JSON to this resource and return the response. * NOTE: the caller is responsible for reading content and closing the response! */ public ClassicHttpResponse post(JSONObject content) throws Exception { HttpPost post=new HttpPost(url); post.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()); if(content!=null)post.setEntity(new StringEntity(content.toString(), ContentType.APPLICATION_JSON)); return execute(post); } /** * post content to this resource, discarding the response */ public void postQuietly(JSONObject content) throws Exception { try(ClassicHttpResponse response = post(content)){ EntityUtils.consume(response.getEntity()); } } /** * delete this resource */ public void delete() throws Exception { HttpDelete d=new HttpDelete(url); try(ClassicHttpResponse response = execute(d)){ EntityUtils.consume(response.getEntity()); } } /** * get the named link * * @param linkName - the name of the link * @return URL of the linked resource * @throws Exception */ public String getLink(String linkName) throws Exception { return getLink(getJSON(), linkName); } /** * get the named link * * @param resourceProperties - the JSON representation of the resource * @param linkName - the name of the link * @return URL of the linked resource * @throws Exception */ public String getLink(JSONObject resourceProperties, String linkName) throws Exception { return resourceProperties.getJSONObject("_links").getJSONObject(linkName).getString("href"); } /** * get the HTTP {@link StatusLine} of the last invocation */ public StatusLine getLastStatus(){ return status; } /** * get the HTTP status code of the last invocation */ public int getLastHttpStatus(){ return status!=null? status.getStatusCode() : -1; } public JSONObject asJSON(ClassicHttpResponse response) throws IOException, JSONException { try{ return new JSONObject(IOUtils.toString(response.getEntity().getContent(), "UTF-8")); } finally{ response.close(); } } public String getSessionKey() { String key = authCallback!=null? authCallback.getSessionKey() : null; String prefsKey = userPreferences!=null? userPreferences.getEncoded() : null; if(key!=null && prefsKey!=null && prefsKey.length()>0) { key = key + prefsKey; } return key; } protected ClassicHttpResponse execute(HttpUriRequestBase method) throws Exception { ClassicHttpResponse response = null; boolean execWithAuth = !useSessions; String sessionKey = null; if(useSessions){ method.removeHeaders(SecuritySessionUtils.SESSION_ID_HEADER); sessionKey = getSessionKey(); String sessionId = sessionIDProvider.getSessionID(url, sessionKey); if(sessionId!=null){ method.setHeader(SecuritySessionUtils.SESSION_ID_HEADER, sessionId); response = client.executeOpen(null, method, HttpClientContext.create()); status = new StatusLine(response); if(432==status.getStatusCode()){ IOUtils.closeQuietly(response); execWithAuth = true; } } else{ execWithAuth = true; } } if(execWithAuth){ method.removeHeaders(SecuritySessionUtils.SESSION_ID_HEADER); response = executeWithAuth(method); } if(useSessions && sessionKey!=null){ try{ Header h = response.getFirstHeader(SecuritySessionUtils.SESSION_ID_HEADER); if(h!=null){ long lifetime = 300*1000; Header lt = response.getFirstHeader(SecuritySessionUtils.SESSION_LIFETIME_HEADER); if(lt!=null){ lifetime = Long.valueOf(lt.getValue()); } sessionIDProvider.registerSession(h.getValue(), method.getUri().toString(), lifetime, sessionKey); } }catch(Exception ex){/*ignored*/} } checkError(response); return response; } protected ClassicHttpResponse executeWithAuth(HttpUriRequestBase method) throws Exception { addAuth(method); addUserPreferences(method); ClassicHttpResponse response = client.executeOpen(null, method, HttpClientContext.create()); status = new StatusLine(response); return response; } /** * Check if the response represents an error (i.e., HTTP status >= 400). * In case of an error, error information is extracted and a RESTException is thrown. * * @param response - HTTP response * @throws RESTException - in case the response represents an error */ public void checkError(ClassicHttpResponse response) throws RESTException { if(errorChecking && response.getCode()>399){ String message = extractErrorMessage(response); IOUtils.closeQuietly(response); throw new RESTException(response.getCode(), response.getReasonPhrase(), message); } } protected String extractErrorMessage(ClassicHttpResponse response) { String errMsg = null; try{ String content = EntityUtils.toString(response.getEntity()); try{ JSONObject jO=new JSONObject(content); for(String key: Arrays.asList("errorMessage", "message")) { errMsg = jO.optString(key, null); if(errMsg!=null)break; } if(errMsg==null)errMsg = jO.toString(); }catch(Exception ex){ errMsg = extractHTMLError(content); } }catch(Exception ex){} return errMsg; } private static final Pattern errorPattern = Pattern.compile("(.*)"); public static String extractHTMLError(String html) { Matcher m = errorPattern.matcher(html); if(m.find())return m.group(1).trim(); else return "n/a"; } public SessionIDProvider getSessionIDProvider() { return sessionIDProvider; } public void setSessionIDProvider(SessionIDProvider sessionIDProvider) { this.sessionIDProvider = sessionIDProvider; } /** * Enable/disable automatic error checking after every request. * Enabled by default - usually the best option. * @param errorChecking */ public void setAutomaticErrorChecking(boolean errorChecking) { this.errorChecking = errorChecking; } public static Map asMap(JSONObject o){ Mapresult=new HashMap<>(); if(o!=null){ Iteratori = o.keys(); while(i.hasNext()){ String key = i.next(); try{ result.put(key, String.valueOf(o.get(key))); }catch(JSONException ex){} } } return result; } public static JSONObject asJSON(Mapmap){ JSONObject o=new JSONObject(); if(map!=null){ for(Map.Entryentry: map.entrySet()){ try{ o.put(entry.getKey(), entry.getValue()); }catch(JSONException e){} } } return o; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy