/* * © Copyright IBM Corp. 2012 * * Licensed 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 com.ibm.sbt.services.client.base; import static com.ibm.sbt.services.client.base.CommonConstants.APPLICATION_ATOM_XML; import static com.ibm.sbt.services.client.base.CommonConstants.AT; import static com.ibm.sbt.services.client.base.CommonConstants.CONTENT_TYPE; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import com.ibm.commons.util.StringUtil; import com.ibm.sbt.services.client.ClientService; import com.ibm.sbt.services.client.ClientService.Handler; import com.ibm.sbt.services.client.ClientServiceListener; import com.ibm.sbt.services.client.ClientServicesException; import com.ibm.sbt.services.client.Response; import com.ibm.sbt.services.client.base.CommonConstants.HTTPCode; import com.ibm.sbt.services.client.base.datahandlers.EntityList; import com.ibm.sbt.services.endpoints.Endpoint; import com.ibm.sbt.services.endpoints.EndpointFactory; /** * This class defines common behaviour for all the services * @author Carlos Manias * */ public abstract class BaseService implements Serializable { private static final long serialVersionUID = 1L; public static final int DEFAULT_CACHE_SIZE = 0; public static final String DEFAULT_ENDPOINT_NAME = "connections"; protected transient static HashMap<String, Object> cache = new HashMap<String, Object>(); protected transient int cacheSize; protected Handler dataFormat; protected transient Endpoint endpoint; protected Map<String, String> headers = new HashMap<String, String>(); protected String[] serviceMappingKeys = new String[]{}; /** * Constructor */ public BaseService() { this(DEFAULT_ENDPOINT_NAME, DEFAULT_CACHE_SIZE); } /** * Constructor * * @param endpointName */ public BaseService(String endpointName) { this(endpointName, DEFAULT_CACHE_SIZE); } /** * Constructor * * @param endpointName * @param cacheSize */ public BaseService(String endpointName, int cacheSize) { if (StringUtil.isEmpty(endpointName)) { endpointName = DEFAULT_ENDPOINT_NAME; } this.endpoint = EndpointFactory.getEndpointFromEnvironment(endpointName, null); this.cacheSize = cacheSize; } /** * Constructor * * @param endpoint */ public BaseService(Endpoint endpoint) { this(endpoint, DEFAULT_CACHE_SIZE); } /** * Constructor * * @param endpoint * @param cacheSize */ public BaseService(Endpoint endpoint, int cacheSize) { this.endpoint = endpoint; this.cacheSize = cacheSize; } /** * Add a default header * * @param name * @param value */ public void addDefaultHeader(String name, String value) { headers.put(name, value); } /** * @return {Handler} dataFormat */ public Handler getDataFormat() { return this.dataFormat; } /** * Sets the endpoint * * @param endpoint * the endpoint */ public void setEndpoint(Endpoint endpoint) { this.endpoint = endpoint; } /** * Returns the Endpoint * * @return endpoint the Endpoint */ public Endpoint getEndpoint() { return endpoint; } /** * Get authentication type for the endpoint. like basicAuth, oauth etc. * @return {NamedUrlPart} */ public NamedUrlPart getAuthType(){ return new NamedUrlPart("authType",AuthType.getAuthTypePart(endpoint)); } /** * Returns a Version object with the API version of the Endpoint * @return {Version} */ public Version getApiVersion(){ return Version.parse(endpoint.getApiVersion()); } /** * Subclasses must list the context roots of the Connections APIs they use. * @return {String[]} */ public String[] getServiceMappingKeys(){ return serviceMappingKeys; } /** * Returns either the configured or the default serviceMappings for the service * @return {NamedUrlPart[]} */ public NamedUrlPart[] getServiceMappings(){ String[] serviceMappingKeys = getServiceMappingKeys(); Map<String, String> serviceMappings = getEndpoint().getServiceMappings(); NamedUrlPart[] urlParts = new NamedUrlPart[serviceMappingKeys.length]; for(int i = 0;i < serviceMappingKeys.length; i++){ String key = serviceMappingKeys[i]; if (serviceMappings.containsKey(key)){ urlParts[i] = new NamedUrlPart(key, serviceMappings.get(key)); }else{ urlParts[i] = new NamedUrlPart(key, key); } } return urlParts; } /** * Return the size of the cache * * @return cacheSize the cache size */ public int getCacheSize() { return cacheSize; } /** * Sets the size of the cache * * @param cacheSize */ public void setCacheSize(int cacheSize) { this.cacheSize = cacheSize; } /* * This method makes a network call and returns an entity */ protected <T extends BaseEntity> T getEntity(String url, Map<String, String> parameters, IFeedHandler<T> feedHandler) throws ClientServicesException { try { Response response = retrieveData(url, parameters); return feedHandler.createEntity(response); } catch (Exception e){ throw new ClientServicesException(e); } } /* * This method makes a network call and returns a Collection of Entities */ protected <T extends BaseEntity> EntityList<T> getEntities(String url, Map<String, String> parameters, IFeedHandler<T> feedHandler) throws ClientServicesException { try { Response dataHolder = retrieveData(url, parameters); return feedHandler.createEntityList(dataHolder); } catch (Exception e){ throw new ClientServicesException(e); } } /* * This method makes a network call and returns a Collection of Entities */ protected <T extends BaseEntity> EntityList<T> getEntities(String url, Map<String, String> parameters, Map<String, String> headers, IFeedHandler<T> feedHandler) throws ClientServicesException { try { Response dataHolder = retrieveData(url, parameters, headers, null); return feedHandler.createEntityList(dataHolder); } catch (Exception e){ throw new ClientServicesException(e); } } /** * Post some data and return the resulting response * * @param serviceUrl * @param parameters * @param headers * @param content * @return {Response} * @throws ClientServicesException */ public Response createData(String serviceUrl, Map<String, String> parameters, Map<String,String> headers, Object content) throws ClientServicesException { return createData(serviceUrl, parameters, headers, content, getDataFormat()); } /** * Post some data and return the resulting response * * @param serviceUrl * @param parameters * @param headers * @param content * @param format * @return {Response} * @throws ClientServicesException */ public Response createData(String serviceUrl, Map<String, String> parameters, Map<String,String> headers, Object content, Handler format) throws ClientServicesException { Response result = getClientService().post(serviceUrl, parameters, headers, content, format); return result; } /** * Creates data and returns the result * * @param serviceUrl * @param parameters * @param content * @param format * @return {Response} the result of the creation operations * @throws ClientServicesException * when the creation fails, as null/false return may have other meaning here */ public Response createData(String serviceUrl, Map<String, String> parameters, Object content, Handler format) throws ClientServicesException { return createData(serviceUrl, parameters, getDefaultHeaders(), content, format); } /** * Creates data and returns the result * @param serviceUrl * @param parameters * @param content * @return {Response} * @throws ClientServicesException */ public Response createData(String serviceUrl, Map<String, String> parameters, Object content) throws ClientServicesException { return createData(serviceUrl, parameters, content, getDataFormat()); } /** * This method encapsulate the access to the Endpoint client service. * * @return {ClientService} the client serivce associated with the current Endpoint * @throws ClientServicesException */ public ClientService getClientService() throws ClientServicesException { return endpoint.getClientService(); } /** * Returns true if delete completed successfully and false if an error happened * * @param serviceUrl * @param parameters * @param nameParameterId * @return {Response} * @throws ClientServicesException */ public Response deleteData(String serviceUrl, Map<String, String> parameters, String nameParameterId) throws ClientServicesException { return deleteData(serviceUrl, parameters, getDefaultHeaders(), nameParameterId); } /** * execute a delete and returns the result * @param serviceUrl * @param parameters * @param headers * @param nameParameterId * @return {Response} * @throws ClientServicesException */ public Response deleteData(String serviceUrl, Map<String, String> parameters, Map<String, String> headers, String nameParameterId) throws ClientServicesException { String uniqueId = null; if (nameParameterId != null) { uniqueId = (parameters == null) ? nameParameterId : parameters.get(nameParameterId); } Response r = getClientService().delete(serviceUrl, parameters, headers, getDataFormat()); if (cacheSize > 0 && uniqueId != null) { removeFromCache(uniqueId); } return r; } public Response delete(String serviceUrl, Map<String, String> parameters, Map<String, String> headers, String nameParameterId, String content)throws ClientServicesException{ String uniqueId = null; if (nameParameterId != null) { uniqueId = (parameters == null) ? nameParameterId : parameters.get(nameParameterId); } Response r = getClientService().delete(serviceUrl, parameters, headers, getDataFormat(),content); if (cacheSize > 0 && uniqueId != null) { removeFromCache(uniqueId); } return r; } /** * retrieveData * * @param url * @param parameters * @return {Response} Convenience method for retrieving multiple JsonObjects from the server * @throws ClientServicesException */ public Response retrieveData(String url, Map<String, String> parameters) throws ClientServicesException { return retrieveData(url, parameters, null); } /** * execute a get and return the result * * @param url * @param parameters * @param headers * @param nameParameterId * @return {Response} * @throws ClientServicesException */ public Response retrieveData(String url, Map<String, String> parameters, Map<String, String> headers, String nameParameterId) throws ClientServicesException { Object data = null; Response dataHolder = null; String uniqueId = ""; if (nameParameterId != null) { uniqueId = parameters.get(nameParameterId); data = findInCache(uniqueId); dataHolder = new Response(data); } if (data == null) { dataHolder = getClientService().get(url, parameters, headers, getDataFormat()); //in case of 401 errors client service returns null if (dataHolder == null) return null; data = dataHolder.getData(); if (cacheSize > 0 && nameParameterId != null) { addDataToCache(uniqueId, data); } } return dataHolder; } /** * retrieveData() * * @param url * @param parameters * @param nameParameterId * @return {Response} Method to retrieve a single Object from the server * @throws ClientServicesException */ public Response retrieveData(String url, Map<String, String> parameters, String nameParameterId) throws ClientServicesException { //TODO: Fix cache with DataHolder object return retrieveData(url, parameters, getDefaultHeaders(), nameParameterId); } /** * Returns true if update completed successfully and false if an error happened * * @param serviceUrl * @param parameters * @param content * @param nameParameterId - only used to distinguish between cache entries, * must be a valid key of the parameters map * @return {Response} * @throws ClientServicesException */ public Response updateData(String serviceUrl, Map<String, String> parameters, Object content, String nameParameterId) throws ClientServicesException { //TODO: Use Args pattern to pass the headers and avoid hard-coding the content-type return updateData(serviceUrl, parameters, getDefaultHeaders(), content, nameParameterId); } /** * execute a put and return the response * * @param serviceUrl * @param parameters * @param headers * @param content * @param nameParameterId * @return {Response} * @throws ClientServicesException */ public Response updateData(String serviceUrl, Map<String, String> parameters, Map<String, String> headers, Object content, String nameParameterId) throws ClientServicesException { String uniqueId = ""; if (nameParameterId != null) { uniqueId = parameters.get(nameParameterId); } if (!headers.containsKey(CONTENT_TYPE)) { // TODO shouldn't assume this headers.put(CONTENT_TYPE, APPLICATION_ATOM_XML); } Response result = getClientService().put(serviceUrl, parameters, headers, content, getDataFormat()); if (cacheSize > 0 && nameParameterId != null) { addDataToCache(uniqueId, content); } return result; } public Response putData(String serviceUrl, Map<String, String> parameters, Map<String, String> headers, Object content, String uniqueId) throws ClientServicesException { Response result = getClientService().put(serviceUrl, parameters, headers, content, getDataFormat()); if (cacheSize > 0 && uniqueId != null) { addDataToCache(uniqueId, content); } return result; } protected void checkResponseCode(Response<?> response, HTTPCode expectedCode) throws ClientServicesException { if (response != null && response.getResponse() != null && response.getRequest()!=null) { if (response.getResponse().getStatusLine() != null && expectedCode.checkCode(response.getResponse().getStatusLine().getStatusCode())) { return; } else { throw new ClientServicesException(response.getResponse(), response.getRequest()); } } else { throw new ClientServicesException(null, "Response is null"); } } /** * finds the object in the cache * * @param key * @return {Object} */ private Object findInCache(String key) { Object data = null; if (cache.containsKey(key)) { data = cache.get(key); } else { // Cache miss } return data; } /** * Removes from cache the data stored under the given key * * @param key */ protected void removeFromCache(String key) { cache.remove(key); } /** * Adds to cache the object using the given key * * @param key * @param content */ protected void addDataToCache(String key, Object content) { // Limit the cache size as per options // to check if cache is full , remove if full using LRU algorithm cache.put(key, content); } /** * Returns a valid parameters HashMap even if null is passed * * @param parameters * @return {Map<String, String>} */ protected Map<String, String> getParameters(Map<String, String> parameters) { if(parameters == null) return new HashMap<String, String>(); else return parameters; } /** * Return default headers * * @return {Map<String, String>} */ protected Map<String, String> getDefaultHeaders() { Map<String, String> defaultHeaders = new HashMap<String, String>(); defaultHeaders.putAll(headers); return defaultHeaders; } /** * Return true if the id as email address * * @param id * @return {boolean} */ protected boolean isEmail(String id) { return (id == null) ? false : id.contains(AT); } }