Java tutorial
/******************************************************************************* * Copyright 2011 Danny Kunz * * 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 org.omnaest.utils.webservice.rest; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import javax.ws.rs.Consumes; import javax.ws.rs.CookieParam; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.MatrixParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.UriBuilder; import org.apache.commons.lang3.ObjectUtils; import org.omnaest.utils.assertion.Assert; import org.omnaest.utils.cache.Cache; import org.omnaest.utils.cache.CacheUtils; import org.omnaest.utils.download.URIHelper; import org.omnaest.utils.proxy.StubCreator; import org.omnaest.utils.proxy.handler.MethodCallCapture; import org.omnaest.utils.proxy.handler.MethodInvocationHandler; import org.omnaest.utils.reflection.ReflectionUtils; import org.omnaest.utils.reflection.ReflectionUtils.MethodParameterMetaInformation; import org.omnaest.utils.structure.array.ArrayUtils; import org.omnaest.utils.structure.collection.set.SetUtils; import org.omnaest.utils.structure.element.factory.Factory; import org.omnaest.utils.structure.map.MapUtils; import org.omnaest.utils.tuple.Tuple2; /** * The {@link RestClientFactory} is an abstract implementation which provides functionality to produce proxies for given class or * interface types which are based on the <a href="http://jsr311.java.net/">JSR-311 API (REST)</a><br> * <br> * The performance is about * <ul> * <li>1000 calls in less than 1s</li> * <li>100000 calls in less than 10s</li> * <li>1000000 calls in less than 20s</li> * </ul> * <br> * Any subclass can use or override the {@link #newRestClient(Class)}, {@link #newRestClient(Class, URI)} and in rare cases * {@link #newRestClient(Class, URI, RestInterfaceMethodInvocationHandler)} * * @see RestClientFactoryJersey * @author Omnaest */ @SuppressWarnings("javadoc") public abstract class RestClientFactory { /* ********************************************** Variables ********************************************** */ private URI baseAddress = null; private RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler = null; private Cache<Class<?>, Tuple2<StubCreator<Object>, RestInterfaceMetaInformation>> stubCreatorAndRestInterfaceMetaInformationCache = CacheUtils .adapter(new ConcurrentHashMap<Class<?>, Tuple2<StubCreator<Object>, RestInterfaceMetaInformation>>()); private Cache<TypeAndBaseAddress, Object> restClientInstanceCache = CacheUtils .adapter(new ConcurrentHashMap<TypeAndBaseAddress, Object>()); /* ********************************************** Classes/Interfaces ********************************************** */ /** * Simple combined entity that implements {@link #hashCode()} and {@link #equals(Object)} * * @author Omnaest */ private static class TypeAndBaseAddress { private final Class<?> type; private final URI baseAddress; public TypeAndBaseAddress(Class<?> type, URI baseAddress) { super(); this.type = type; this.baseAddress = baseAddress; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("TypeAndBaseAddress [type="); builder.append(this.type); builder.append(", baseAddress="); builder.append(this.baseAddress); builder.append("]"); return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.baseAddress == null) ? 0 : this.baseAddress.hashCode()); result = prime * result + ((this.type == null) ? 0 : this.type.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof TypeAndBaseAddress)) { return false; } TypeAndBaseAddress other = (TypeAndBaseAddress) obj; if (this.baseAddress == null) { if (other.baseAddress != null) { return false; } } else if (!this.baseAddress.equals(other.baseAddress)) { return false; } if (this.type == null) { if (other.type != null) { return false; } } else if (!this.type.equals(other.type)) { return false; } return true; } } /** * Handler for {@link Method} invocations on the generated proxy for a JSR-311 type * * @author Omnaest */ public static interface RestInterfaceMethodInvocationHandler { /** * @see HttpMethod * @see Parameter * @param baseAddress * @param pathRelative * @param httpMethod * @param parameterList * @param returnType * @param consumesMediaTypes * media types in order method declaration, type declaration * @param producesMediaTypes * media types in order method declaration, type declaration * @return */ public <T> T handleMethodInvocation(URI baseAddress, String pathRelative, HttpMethod httpMethod, List<Parameter> parameterList, Class<T> returnType, String[] consumesMediaTypes, String[] producesMediaTypes); } /** * Representation of a method parameter. See the derived types. <br> * E.g.: * * <pre> * if ( parameter instanceof QueryParameter ) * { * QueryParameter queryParameter = (QueryParameter) parameter; * ... * } * else if (...) * { * ... * } * </pre> * * @see BodyParameter * @see QueryParameter * @see MatrixParameter * @author Omnaest */ public static interface Parameter { } /** * Representation of a {@link Parameter} without any annotation * * @author Omnaest */ public static class BodyParameter implements Parameter { /* ********************************************** Variables ********************************************** */ private Object value = null; /* ********************************************** Methods ********************************************** */ public BodyParameter(Object value) { super(); this.value = value; } public Object getValue() { return this.value; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("BodyParameter [value="); builder.append(this.value); builder.append("]"); return builder.toString(); } } /** * Representation of a {@link QueryParam} annotated method parameter * * @author Omnaest */ public static class QueryParameter extends KeyAndValueParameter { public QueryParameter(String key, String value) { super(key, value); } } /** * Representation of a {@link CookieParam} annotated method parameter * * @author Omnaest */ public static class CookieParameter implements Parameter { private final Cookie cookie; public CookieParameter(Cookie cookie) { super(); this.cookie = cookie; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("CookieParameter [cookie="); builder.append(this.cookie); builder.append("]"); return builder.toString(); } public Cookie getCookie() { return this.cookie; } } /** * Representation of a {@link HeaderParam} annotated method parameter * * @author Omnaest */ public static class HeaderParameter extends KeyAndValueParameter { public HeaderParameter(String key, String value) { super(key, value); } } /** * Representation of a {@link QueryParam} annotated method parameter * * @author Omnaest */ private static abstract class KeyAndValueParameter implements Parameter { /* ********************************************** Variables ********************************************** */ private String key = null; private String value = null; /* ********************************************** Methods ********************************************** */ public String getKey() { return this.key; } public String getValue() { return this.value; } public KeyAndValueParameter(String key, String value) { super(); this.key = key; this.value = value; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("KeyAndValueParameter [key="); builder.append(this.key); builder.append(", value="); builder.append(this.value); builder.append("]"); return builder.toString(); } } /** * Representation of a {@link MatrixParam} annotated method parameter * * @author Omnaest */ public static class MatrixParameter implements Parameter { /* ********************************************** Variables ********************************************** */ private String key = null; private Collection<Object> valueCollection = null; /* ********************************************** Methods ********************************************** */ public MatrixParameter(String key, Collection<Object> valueCollection) { super(); this.key = key; this.valueCollection = valueCollection; } public String getKey() { return this.key; } public Collection<Object> getValueCollection() { return this.valueCollection; } } /** * The {@link RestInterfaceMetaInformation} will hold meta information related to the annotations used by the JSR-311 API * * @author Omnaest */ protected static class RestInterfaceMetaInformation { /* ********************************************** Variables ********************************************** */ private final RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass; private final Map<Method, RestInterfaceMetaInformationForMethod> methodToRestInterfaceMetaInformationForMethodMap; /* ********************************************** Methods ********************************************** */ /** * @see RestInterfaceMetaInformation * @param restInterfaceMetaInformationForClass * @param methodToRestInterfaceMetaInformationForMethodMap */ public RestInterfaceMetaInformation( RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass, Map<Method, RestInterfaceMetaInformationForMethod> methodToRestInterfaceMetaInformationForMethodMap) { super(); this.restInterfaceMetaInformationForClass = restInterfaceMetaInformationForClass; this.methodToRestInterfaceMetaInformationForMethodMap = Collections .unmodifiableMap(methodToRestInterfaceMetaInformationForMethodMap); } public RestInterfaceMetaInformationForClass getRestInterfaceMetaInformationForClass() { return this.restInterfaceMetaInformationForClass; } public Map<Method, RestInterfaceMetaInformationForMethod> getMethodToRestInterfaceMetaInformationForMethodMap() { return this.methodToRestInterfaceMetaInformationForMethodMap; } } protected static class RestInterfaceMetaInformationForClass { /* ********************************************** Variables ********************************************** */ protected Class<?> type = null; protected String path = null; protected List<String> mediaTypeProducesList = new ArrayList<String>(); protected List<String> mediaTypeConsumesList = new ArrayList<String>(); /* ********************************************** Methods ********************************************** */ public String getPath() { return this.path; } public void setPath(String path) { this.path = path; } public List<String> getMediaTypeProducesList() { return this.mediaTypeProducesList; } public List<String> getMediaTypeConsumesList() { return this.mediaTypeConsumesList; } public void setType(Class<?> type) { this.type = type; } public Class<?> getType() { return this.type; } } /** * @author Omnaest */ public static enum HttpMethod { GET, PUT, POST, DELETE } protected static class RestInterfaceMetaInformationForMethod extends RestInterfaceMetaInformationForClass { /* ********************************************** Variables ********************************************** */ private Method method = null; private HttpMethod httpMethod = null; private final SortedMap<Integer, String> parameterIndexPositionToQueryParameterTagMap = new TreeMap<Integer, String>(); private final SortedMap<Integer, String> parameterIndexPositionToPathParameterTagMap = new TreeMap<Integer, String>(); private final SortedMap<Integer, String> parameterIndexPositionToMatrixParameterTagMap = new TreeMap<Integer, String>(); private final SortedMap<Integer, String> parameterIndexPositionToHeaderParameterTagMap = new TreeMap<Integer, String>(); private final SortedMap<Integer, String> parameterIndexPositionToCookieParameterTagMap = new TreeMap<Integer, String>(); /* ********************************************** Methods ********************************************** */ public HttpMethod getHttpMethod() { return this.httpMethod; } public void setHttpMethod(HttpMethod httpMethod) { this.httpMethod = httpMethod; } public SortedMap<Integer, String> getParameterIndexPositionToQueryParameterTagMap() { return this.parameterIndexPositionToQueryParameterTagMap; } public SortedMap<Integer, String> getParameterIndexPositionToPathParameterTagMap() { return this.parameterIndexPositionToPathParameterTagMap; } public SortedMap<Integer, String> getParameterIndexPositionToMatrixParameterTagMap() { return this.parameterIndexPositionToMatrixParameterTagMap; } public Method getMethod() { return this.method; } public void setMethod(Method method) { this.method = method; } public SortedMap<Integer, String> getParameterIndexPositionToHeaderParameterTagMap() { return this.parameterIndexPositionToHeaderParameterTagMap; } public SortedMap<Integer, String> getParameterIndexPositionToCookieParameterTagMap() { return this.parameterIndexPositionToCookieParameterTagMap; } } protected class RestClientMethodInvocationHandler implements MethodInvocationHandler { /* ********************************************** Variables ********************************************** */ protected final RestInterfaceMetaInformation restInterfaceMetaInformation; @SuppressWarnings("hiding") protected final URI baseAddress; @SuppressWarnings("hiding") protected final RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler; /* ********************************************** Methods ********************************************** */ public RestClientMethodInvocationHandler(RestInterfaceMetaInformation restInterfaceMetaInformation, URI baseAddress, RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler) { super(); this.restInterfaceMetaInformation = restInterfaceMetaInformation; this.baseAddress = baseAddress; this.restInterfaceMethodInvocationHandler = restInterfaceMethodInvocationHandler; } @Override public Object handle(MethodCallCapture methodCallCapture) throws Throwable { // Object retval = null; // Class<?> returnType = methodCallCapture.getMethod().getReturnType(); @SuppressWarnings("unchecked") boolean hasSubResourceReturnType = ReflectionUtils.hasDeclaredAnnotation(returnType, Path.class) || ReflectionUtils.hasAnnotationOnAnyMethod(returnType, Path.class, GET.class, PUT.class, POST.class, DELETE.class, Consumes.class, Produces.class); // final URI baseAddress = this.baseAddress; final Method method = methodCallCapture.getMethod(); final RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass = this.restInterfaceMetaInformation .getRestInterfaceMetaInformationForClass(); final RestInterfaceMetaInformationForMethod restInterfaceMetaInformationForMethod = this.restInterfaceMetaInformation .getMethodToRestInterfaceMetaInformationForMethodMap().get(method); final Object[] arguments = methodCallCapture.getArguments(); final String relativePath = this.buildRelativePath(restInterfaceMetaInformationForClass, restInterfaceMetaInformationForMethod, arguments); // if (hasSubResourceReturnType) { // URI newBaseAddress = URIHelper.createUri(baseAddress, relativePath); retval = newRestClient(returnType, newBaseAddress); } else { // final RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler = this.restInterfaceMethodInvocationHandler; if (restInterfaceMethodInvocationHandler != null) { // final List<Parameter> parameterList = this.buildParamterList( restInterfaceMetaInformationForClass, restInterfaceMetaInformationForMethod, arguments); final HttpMethod httpMethod = restInterfaceMetaInformationForMethod.getHttpMethod(); final String[] consumesMediaTypes = this.determineConsumesMediaTypes( restInterfaceMetaInformationForClass, restInterfaceMetaInformationForMethod); final String[] producesMediaTypes = this.determineProducesMediaTypes( restInterfaceMetaInformationForClass, restInterfaceMetaInformationForMethod); if (relativePath != null && httpMethod != null && parameterList != null) { retval = restInterfaceMethodInvocationHandler.handleMethodInvocation(baseAddress, relativePath, httpMethod, parameterList, returnType, consumesMediaTypes, producesMediaTypes); } } } // return retval; } protected String[] determineConsumesMediaTypes( RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass, RestInterfaceMetaInformationForMethod restInterfaceMetaInformationForMethod) { // List<String> mediaTypeConsumesList = new ArrayList<String>(); mediaTypeConsumesList.addAll(restInterfaceMetaInformationForMethod.getMediaTypeConsumesList()); mediaTypeConsumesList.addAll(restInterfaceMetaInformationForClass.getMediaTypeConsumesList()); // return new LinkedHashSet<String>(mediaTypeConsumesList).toArray(new String[0]); } protected String[] determineProducesMediaTypes( RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass, RestInterfaceMetaInformationForMethod restInterfaceMetaInformationForMethod) { // List<String> mediaTypeProducesList = new ArrayList<String>(); mediaTypeProducesList.addAll(restInterfaceMetaInformationForMethod.getMediaTypeProducesList()); mediaTypeProducesList.addAll(restInterfaceMetaInformationForClass.getMediaTypeProducesList()); // return new LinkedHashSet<String>(mediaTypeProducesList).toArray(new String[0]); } protected List<Parameter> buildParamterList( RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass, RestInterfaceMetaInformationForMethod restInterfaceMetaInformationForMethod, Object[] arguments) { // List<Parameter> retlist = new ArrayList<Parameter>(); // { // final SortedMap<Integer, String> parameterIndexPositionToQueryParameterTagMap = restInterfaceMetaInformationForMethod .getParameterIndexPositionToQueryParameterTagMap(); for (Integer parameterIndexPosition : parameterIndexPositionToQueryParameterTagMap.keySet()) { if (parameterIndexPosition != null && parameterIndexPosition < arguments.length) { final String key = parameterIndexPositionToQueryParameterTagMap.get(parameterIndexPosition); final Object value = arguments[parameterIndexPosition]; retlist.add(new QueryParameter(key, ObjectUtils.toString(value))); } } } { // final SortedMap<Integer, String> parameterIndexPositionToCookieParameterTagMap = restInterfaceMetaInformationForMethod .getParameterIndexPositionToCookieParameterTagMap(); for (Integer parameterIndexPosition : parameterIndexPositionToCookieParameterTagMap.keySet()) { if (parameterIndexPosition != null && parameterIndexPosition < arguments.length) { final String key = parameterIndexPositionToCookieParameterTagMap .get(parameterIndexPosition); final Object value = arguments[parameterIndexPosition]; if (value instanceof Cookie) { retlist.add(new CookieParameter((Cookie) value)); } else { retlist.add(new CookieParameter(new Cookie(key, ObjectUtils.toString(value)))); } } } } { // final SortedMap<Integer, String> parameterIndexPositionToHeaderParameterTagMap = restInterfaceMetaInformationForMethod .getParameterIndexPositionToHeaderParameterTagMap(); for (Integer parameterIndexPosition : parameterIndexPositionToHeaderParameterTagMap.keySet()) { if (parameterIndexPosition != null && parameterIndexPosition < arguments.length) { final String key = parameterIndexPositionToHeaderParameterTagMap .get(parameterIndexPosition); final Object value = arguments[parameterIndexPosition]; retlist.add(new HeaderParameter(key, ObjectUtils.toString(value))); } } } { // final SortedMap<Integer, String> parameterIndexPositionToMatrixParameterTagMap = restInterfaceMetaInformationForMethod .getParameterIndexPositionToMatrixParameterTagMap(); for (Integer parameterIndexPosition : parameterIndexPositionToMatrixParameterTagMap.keySet()) { if (parameterIndexPosition != null && parameterIndexPosition < arguments.length) { // String key = parameterIndexPositionToMatrixParameterTagMap.get(parameterIndexPosition); Object value = arguments[parameterIndexPosition]; // if (value instanceof Collection) { @SuppressWarnings("unchecked") final Collection<Object> valueCollection = (Collection<Object>) value; retlist.add(new MatrixParameter(key, valueCollection)); } else if (value != null) { retlist.add(new MatrixParameter(key, Arrays.asList(value))); } } } } // { // @SuppressWarnings("unchecked") final Set<Integer> alreadyProcessedIndexPositionSet = SetUtils.mergeAll( restInterfaceMetaInformationForMethod.getParameterIndexPositionToCookieParameterTagMap() .keySet(), restInterfaceMetaInformationForMethod.getParameterIndexPositionToHeaderParameterTagMap() .keySet(), restInterfaceMetaInformationForMethod.getParameterIndexPositionToMatrixParameterTagMap() .keySet(), restInterfaceMetaInformationForMethod.getParameterIndexPositionToPathParameterTagMap() .keySet(), restInterfaceMetaInformationForMethod.getParameterIndexPositionToQueryParameterTagMap() .keySet()); for (int indexPosition = 0; indexPosition < arguments.length; indexPosition++) { if (!alreadyProcessedIndexPositionSet.contains(indexPosition)) { final Object value = arguments[indexPosition]; retlist.add(new BodyParameter(value)); } } } // return retlist; } protected String buildRelativePath( RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass, RestInterfaceMetaInformationForMethod restInterfaceMetaInformationForMethod, Object[] arguments) { // String retval = null; // final boolean hasPathAnnotationOnType = ReflectionUtils .hasAnnotation(restInterfaceMetaInformationForClass.getType(), Path.class); UriBuilder uriBuilder = hasPathAnnotationOnType ? UriBuilder.fromResource(restInterfaceMetaInformationForClass.getType()) : UriBuilder.fromPath(""); // final boolean hasDeclaredPathAnnotation = ReflectionUtils .hasDeclaredAnnotation(restInterfaceMetaInformationForMethod.getMethod(), Path.class); if (hasDeclaredPathAnnotation) { uriBuilder = uriBuilder.path(restInterfaceMetaInformationForMethod.getMethod()); } // { final SortedMap<Integer, String> parameterIndexPositionToMatrixParameterTagMap = restInterfaceMetaInformationForMethod .getParameterIndexPositionToMatrixParameterTagMap(); for (Integer indexPosition : parameterIndexPositionToMatrixParameterTagMap.keySet()) { if (indexPosition != null && indexPosition < arguments.length) { // final String key = parameterIndexPositionToMatrixParameterTagMap.get(indexPosition); final Object argument = arguments[indexPosition]; // Object[] values = new Object[] {}; if (ArrayUtils.isArray(argument)) { values = (Object[]) argument; } else if (argument instanceof Collection) { @SuppressWarnings("unchecked") final Collection<Object> collection = (Collection<Object>) argument; values = collection.toArray(); } else { values = new Object[] { argument }; } uriBuilder.matrixParam(key, values); } } } // Map<String, Object> pathKeyToValueMap = new LinkedHashMap<String, Object>(); { // final SortedMap<Integer, String> parameterIndexPositionToPathParameterTagMap = restInterfaceMetaInformationForMethod .getParameterIndexPositionToPathParameterTagMap(); for (Integer indexPosition : parameterIndexPositionToPathParameterTagMap.keySet()) { if (indexPosition != null && indexPosition < arguments.length) { // String key = parameterIndexPositionToPathParameterTagMap.get(indexPosition); Object value = arguments[indexPosition]; // pathKeyToValueMap.put(key, value); } } } // URI uri = uriBuilder.buildFromMap(pathKeyToValueMap); retval = uri.toString(); // return retval; } } /* ********************************************** Methods ********************************************** */ /** * This constructor should be used only in cases where no default {@link RestInterfaceMethodInvocationHandler} should be used.<br> * <br> * Without the default instance the {@link RestClientFactory#newRestClient(Class)} and * {@link RestClientFactory#newRestClient(Class, URI)} cannot be used. Instead the * {@link RestClientFactory#newRestClient(Class, URI, RestInterfaceMethodInvocationHandler)} has to be called with a appropriate * instance. * * @see RestClientFactory */ protected RestClientFactory() { this(null, null); } /** * @see RestClientFactory * @param restInterfaceMethodInvocationHandler * {@link RestInterfaceMethodInvocationHandler} */ protected RestClientFactory(RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler) { this(null, restInterfaceMethodInvocationHandler); } /** * @param baseAddress * @param restInterfaceMethodInvocationHandler * {@link RestInterfaceMethodInvocationHandler} */ protected RestClientFactory(String baseAddress, RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler) { super(); // try { this.baseAddress = baseAddress == null ? null : new URI(baseAddress); } catch (URISyntaxException e) { } this.restInterfaceMethodInvocationHandler = restInterfaceMethodInvocationHandler; } /** * Factory method for new REST client proxy instances.<br> * <br> * This uses the base address given to the instance constructor. If no base address is given this throws an * {@link IllegalArgumentException}. * * @param type * @return */ protected <T> T newRestClient(Class<T> type, final RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler) { Assert.isNotNull(this.baseAddress, "base address given to the constructor must not be null"); return newRestClient(type, this.baseAddress, restInterfaceMethodInvocationHandler); } protected <T> T newRestClient(Class<T> type) { Assert.isNotNull(this.baseAddress, "base address given to the constructor must not be null"); return newRestClient(type, this.baseAddress); } /** * Factory method for new REST client proxy instances. * * @param type * @param baseAddress * @return */ protected <T> T newRestClient(final Class<T> type, final URI baseAddress) { Assert.isNotNull(this.restInterfaceMethodInvocationHandler, "A RestInterfaceMethodInvocationHandler instance has to be provided at constructor time of the RestClientFactory"); return this.newRestClient(type, baseAddress, this.restInterfaceMethodInvocationHandler); } /** * Factory method for new REST client proxy instances. <br> * <br> * This method can be used if there was no default {@link RestInterfaceMethodInvocationHandler} set at constructor time * * @param type * @param baseAddress * @param restInterfaceMethodInvocationHandler * {@link RestInterfaceMethodInvocationHandler} * @return */ @SuppressWarnings({ "unchecked" }) protected <T> T newRestClient(final Class<T> type, final URI baseAddress, final RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler) { Assert.isNotNull(restInterfaceMethodInvocationHandler, "restInterfaceMethodInvocationHandler instance must not be null"); Assert.isNotNull(baseAddress, "baseAddress must not be null"); Assert.isNotNull(type, "type must not be null"); // T retval = null; if (type != null && baseAddress != null) { final Factory<Object> restClientfactory = new Factory<Object>() { @SuppressWarnings("cast") @Override public Object newInstance() { // Tuple2<StubCreator<Object>, RestInterfaceMetaInformation> cachedStubCreatorAndMetaInfo = RestClientFactory.this.stubCreatorAndRestInterfaceMetaInformationCache .getOrCreate(type, new Factory<Tuple2<StubCreator<Object>, RestInterfaceMetaInformation>>() { @Override public Tuple2<StubCreator<Object>, RestInterfaceMetaInformation> newInstance() { final Class<?>[] interfaces = type.getInterfaces(); final RestInterfaceMetaInformation restInterfaceMetaInformation = analyzeTheRestInterfaceMetaInformation( type); return new Tuple2<StubCreator<Object>, RestInterfaceMetaInformation>( new StubCreator<Object>(type, interfaces), restInterfaceMetaInformation); } }); final RestInterfaceMetaInformation restInterfaceMetaInformation = cachedStubCreatorAndMetaInfo .getValueSecond(); final MethodInvocationHandler methodInvocationHandler = new RestClientMethodInvocationHandler( restInterfaceMetaInformation, baseAddress, restInterfaceMethodInvocationHandler); final StubCreator<Object> stubCreator = cachedStubCreatorAndMetaInfo.getValueFirst(); return (T) stubCreator.build(methodInvocationHandler); } }; final boolean isCacheable = restInterfaceMethodInvocationHandler == this.restInterfaceMethodInvocationHandler; if (isCacheable) { retval = (T) this.restClientInstanceCache.getOrCreate(new TypeAndBaseAddress(type, baseAddress), restClientfactory); } else { retval = (T) restClientfactory.newInstance(); } } return retval; } protected static <T> RestInterfaceMetaInformation analyzeTheRestInterfaceMetaInformation(Class<T> type) { final RestInterfaceMetaInformationForClass restInterfaceMetaInformationForClass = new RestInterfaceMetaInformationForClass(); final Map<Method, RestInterfaceMetaInformationForMethod> methodToRestInterfaceMetaInformationForMethodMap = new HashMap<Method, RestInterfaceMetaInformationForMethod>(); { { // restInterfaceMetaInformationForClass.setType(type); // List<Annotation> declaredAnnotationList = ReflectionUtils.declaredAnnotationList(type); for (Annotation annotation : declaredAnnotationList) { if (annotation instanceof Path) { // Path path = (Path) annotation; String pathValue = path.value(); restInterfaceMetaInformationForClass.setPath(pathValue); } else if (annotation instanceof Produces) { // Produces produces = (Produces) annotation; String[] values = produces.value(); restInterfaceMetaInformationForClass.getMediaTypeProducesList() .addAll(Arrays.asList(values)); } else if (annotation instanceof Consumes) { // Consumes consumes = (Consumes) annotation; String[] values = consumes.value(); restInterfaceMetaInformationForClass.getMediaTypeConsumesList() .addAll(Arrays.asList(values)); } } } { // List<Method> methodList = ReflectionUtils.methodListIncludingSuperMethods(type); for (Method method : methodList) { // RestInterfaceMetaInformationForMethod restInterfaceMetaInformationForMethod = new RestInterfaceMetaInformationForMethod(); { // restInterfaceMetaInformationForMethod.setMethod(method); // { // Set<Annotation> declaredAnnotationSet = ReflectionUtils.declaredAnnotationSet(method); for (Annotation annotation : declaredAnnotationSet) { if (annotation instanceof Path) { // Path path = (Path) annotation; String pathValue = path.value(); restInterfaceMetaInformationForMethod.setPath(pathValue); } else if (annotation instanceof GET) { HttpMethod httpMethod = HttpMethod.GET; restInterfaceMetaInformationForMethod.setHttpMethod(httpMethod); } else if (annotation instanceof PUT) { HttpMethod httpMethod = HttpMethod.PUT; restInterfaceMetaInformationForMethod.setHttpMethod(httpMethod); } else if (annotation instanceof POST) { HttpMethod httpMethod = HttpMethod.POST; restInterfaceMetaInformationForMethod.setHttpMethod(httpMethod); } else if (annotation instanceof DELETE) { HttpMethod httpMethod = HttpMethod.DELETE; restInterfaceMetaInformationForMethod.setHttpMethod(httpMethod); } else if (annotation instanceof Produces) { // Produces produces = (Produces) annotation; String[] values = produces.value(); restInterfaceMetaInformationForMethod.getMediaTypeProducesList() .addAll(Arrays.asList(values)); } else if (annotation instanceof Consumes) { // Consumes consumes = (Consumes) annotation; String[] values = consumes.value(); restInterfaceMetaInformationForMethod.getMediaTypeConsumesList() .addAll(Arrays.asList(values)); } } } // { // List<MethodParameterMetaInformation> declaredMethodParameterMetaInformationList = ReflectionUtils .declaredMethodParameterMetaInformationList(method); int indexPosition = 0; for (MethodParameterMetaInformation methodParameterMetaInformation : declaredMethodParameterMetaInformationList) { // List<Annotation> declaredAnnotationList = methodParameterMetaInformation .getDeclaredAnnotationList(); for (Annotation annotation : declaredAnnotationList) { if (annotation instanceof QueryParam) { // QueryParam queryParam = (QueryParam) annotation; String value = queryParam.value(); restInterfaceMetaInformationForMethod .getParameterIndexPositionToQueryParameterTagMap() .put(indexPosition, value); } else if (annotation instanceof PathParam) { // PathParam pathParam = (PathParam) annotation; String value = pathParam.value(); restInterfaceMetaInformationForMethod .getParameterIndexPositionToPathParameterTagMap() .put(indexPosition, value); } else if (annotation instanceof MatrixParam) { // MatrixParam matrixParam = (MatrixParam) annotation; String value = matrixParam.value(); restInterfaceMetaInformationForMethod .getParameterIndexPositionToMatrixParameterTagMap() .put(indexPosition, value); } else if (annotation instanceof HeaderParam) { // HeaderParam headerParam = (HeaderParam) annotation; String value = headerParam.value(); restInterfaceMetaInformationForMethod .getParameterIndexPositionToHeaderParameterTagMap() .put(indexPosition, value); } else if (annotation instanceof CookieParam) { // CookieParam cookieParam = (CookieParam) annotation; String value = cookieParam.value(); restInterfaceMetaInformationForMethod .getParameterIndexPositionToCookieParameterTagMap() .put(indexPosition, value); } } // indexPosition++; } } } methodToRestInterfaceMetaInformationForMethodMap.put(method, restInterfaceMetaInformationForMethod); } } } // return new RestInterfaceMetaInformation(restInterfaceMetaInformationForClass, methodToRestInterfaceMetaInformationForMethodMap); } protected void setBaseAddress(URI baseAddress) { this.baseAddress = baseAddress; } protected void setRestInterfaceMethodInvocationHandler( RestInterfaceMethodInvocationHandler restInterfaceMethodInvocationHandler) { this.restInterfaceMethodInvocationHandler = restInterfaceMethodInvocationHandler; } }