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

net.anthavio.httl.HttlSender Maven / Gradle / Ivy

The newest version!
package net.anthavio.httl;

import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import net.anthavio.httl.HttlBuilder.TransportChooser;
import net.anthavio.httl.HttlExecutionChain.SenderExecutionChain;
import net.anthavio.httl.HttlRequest.Method;
import net.anthavio.httl.HttlRequestBuilder.BodyfulRequestBuilder;
import net.anthavio.httl.HttlRequestBuilder.BodylessRequestBuilder;
import net.anthavio.httl.HttlResponseExtractor.ExtractedResponse;
import net.anthavio.httl.cache.CachedResponse;
import net.anthavio.httl.util.Cutils;
import net.anthavio.httl.util.GenericType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Immutable
 * 
 * @author martin.vanek
 *
 */
public class HttlSender implements SenderOperations, Closeable {

	/**
	 * Initiate HttlSender Builder...
	 * 
	 * @param url 
	 * @return Ultimate builder/configurer
	 */
	public static TransportChooser url(String url) {
		return HttlBuilder.sender(url);
	}

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	private final HttlTransport transport;

	private final SenderConfigurer config;

	private final ExecutorService executor; //can be null

	private final HttlBodyMarshaller marshaller;

	private final HttlBodyUnmarshaller unmarshaller;

	private final List executionFilters;

	public HttlSender(SenderConfigurer config) {
		if (config == null) {
			throw new IllegalArgumentException("null config");
		}
		this.config = config;
		this.transport = config.getTransport();
		this.executor = config.getExecutorService();
		this.marshaller = config.getMarshaller();
		this.unmarshaller = config.getUnmarshaller();
		this.executionFilters = config.getExecutionFilters();
	}

	public HttlTransport getTransport() {
		return transport;
	}

	public ExecutorService getExecutor() {
		return executor;
	}

	public HttlBodyMarshaller getMarshaller() {
		return marshaller;
	}

	public HttlBodyUnmarshaller getUnmarshaller() {
		return unmarshaller;
	}

	public SenderConfigurer getConfig() {
		return config;
	}

	public void close() {
		transport.close();
	}

	HttlResponse doExecute(HttlRequest request) throws IOException {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug(request.getMethod() + " " + request.getUrl());
		}
		return transport.call(request);
	}

	/**
	 * Execute Request and return raw unprocessed Response.
	 * Response is left open and caller is responsibe for closing it.
	 */
	public HttlResponse execute(HttlRequest request) throws HttlRequestException {

		HttlResponse response = null;
		try {
			if (executionFilters != null && executionFilters.size() != 0) {
				response = new SenderExecutionChain(executionFilters, this).next(request);
			} else {
				response = doExecute(request);
			}
		} catch (Exception x) {
			Cutils.close(response);
			if (x instanceof RuntimeException) {
				throw (RuntimeException) x;
			} else {
				throw new HttlRequestException(x);
			}

		}

		return response;
	}

	/**
	 * Execute Request and use ResponseHandler parameter to process Response.
	 * Response is closed automaticaly.
	 * 
	 */
	public void execute(HttlRequest request, HttlResponseHandler handler) throws HttlException {
		if (handler == null) {
			throw new IllegalArgumentException("null handler");
		}
		HttlResponse response = null;
		try {
			response = execute(request);
		} catch (HttlException sx) {
			handler.onFailure(request, sx.getException());
			return;
		} catch (Exception x) {
			handler.onFailure(request, x);
			return;
		}

		try {
			handler.onResponse(response);
		} catch (Exception x) {
			if (x instanceof RuntimeException) {
				throw (RuntimeException) x;
			} else {
				throw new HttlResponseException(response, x);
			}
		} finally {
			Cutils.close(response);
		}

	}

	/**
	 * Extracted response version. Response is extracted, closed and result is returned to caller
	 * ResponseExtractor is created and used to extract the specified resultType
	 */
	public  ExtractedResponse extract(HttlRequest request, Class resultType) throws HttlException {
		if (resultType == null) {
			throw new IllegalArgumentException("resultType is null");
		}
		HttlResponse response = execute(request);
		return extract(response, resultType);
	}

	/**
	 * To overcome Java generics shortcomings use HttlSender#extract(request, new GenericType> {})
	 * to extract Generic collection
	 */
	public  ExtractedResponse extract(HttlRequest request, GenericType resultType) throws HttlException {
		if (resultType == null) {
			throw new IllegalArgumentException("resultType is null");
		}
		HttlResponse response = execute(request);
		return extract(response, resultType.getParameterizedType());
	}

	/**
	 * Mainly for CachingSender and CachingExtractor to access HttpSenders extracting facilities
	 */
	public  ExtractedResponse extract(HttlResponse response, Type resultType) throws HttlException {
		try {
			Object payload;
			if (resultType.equals(String.class)) {
				payload = config.getStringExtractor().extract(response);
			} else if (resultType.equals(byte[].class)) {
				payload = config.getBytesExtractor().extract(response);
			} else {
				payload = this.unmarshaller.unmarshall(response, resultType);
			}
			return new ExtractedResponse(response, (T) payload);//XXX this cast is erased!

		} catch (IOException iox) {
			throw new HttlResponseException(response, iox);
		} finally {
			Cutils.close(response);
		}
	}

	/**
	 * Extracted response version. Response is extracted, closed and result is returned to caller.
	 * 
	 * Response is closed automaticaly.
	*/
	public  ExtractedResponse extract(HttlRequest request, HttlResponseExtractor extractor) throws HttlException {
		if (extractor == null) {
			throw new IllegalArgumentException("ResponseExtractor is null");
		}
		HttlResponse response = null;
		try {
			response = execute(request);
			T extracted = (T) extractor.extract(response);
			return new ExtractedResponse(response, extracted);
			/*
				Class resultType = (Class) ((ParameterizedType) extractor.getClass().getGenericSuperclass())
						.getActualTypeArguments()[0];
				Object extract = unmarshaller.unmarshall(response, resultType);
				if (extract instanceof RuntimeException) {
					throw (RuntimeException) extract;
				} else if (extract instanceof Exception) {
					throw new HttlProcessingException(response, (Exception) extract);
				} else {
					return new ExtractedResponse(response, (T) extract);
				}
			*/
		} catch (Exception x) {
			throw new HttlResponseException(response, x);
		} finally {
			Cutils.close(response);
		}
	}

	/**
	 * Asynchronous extraction with Future as response
	 */
	public  Future> start(final HttlRequest request, final HttlResponseExtractor extractor) {
		if (executor == null) {
			throw new IllegalStateException("Executor for asynchronous requests is not configured");
		}
		return executor.submit(new Callable>() {

			@Override
			public ExtractedResponse call() throws Exception {
				return extract(request, extractor);
			}
		});
	}

	/**
	 * Asynchronous extraction with Future as response
	 */
	public  Future> start(final HttlRequest request, final Class resultType) {
		if (executor == null) {
			throw new IllegalStateException("Executor for asynchronous requests is not configured");
		}
		return executor.submit(new Callable>() {

			@Override
			public ExtractedResponse call() throws Exception {
				return extract(request, resultType);
			}
		});
	}

	/**
	 * Asynchronous execution whith ResponseHandler
	 */
	public void start(final HttlRequest request, final HttlResponseHandler handler) {
		if (executor == null) {
			throw new IllegalStateException("Executor for asynchronous requests is not configured");
		}
		executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					execute(request, handler);
				} catch (Exception x) {
					logger.warn("Failed asynchronous request", x);
				}
			}
		});
	}

	/**
	 * Asynchronous execution with Future as response
	 */
	public Future start(final HttlRequest request) {
		if (executor == null) {
			throw new IllegalStateException("Executor for asynchronous requests is not configured");
		}
		return executor.submit(new Callable() {

			@Override
			public HttlResponse call() throws Exception {
				HttlResponse response = execute(request);
				//too dangerous to left the SenderResponse unclosed
				//we will cache the response...
				return new CachedResponse(request, response);
			}
		});
	}

	/**
	 * Fluent builders 
	 */

	public BodylessRequestBuilder GET(String path) {
		return new BodylessRequestBuilder(this, Method.GET, path);
	}

	public BodylessRequestBuilder HEAD(String path) {
		return new BodylessRequestBuilder(this, Method.HEAD, path);
	}

	public BodylessRequestBuilder TRACE(String path) {
		return new BodylessRequestBuilder(this, Method.TRACE, path);
	}

	public BodylessRequestBuilder OPTIONS(String path) {
		return new BodylessRequestBuilder(this, Method.OPTIONS, path);
	}

	public BodylessRequestBuilder DELETE(String path) {
		return new BodylessRequestBuilder(this, Method.DELETE, path);
	}

	public BodyfulRequestBuilder POST(String path) {
		return new BodyfulRequestBuilder(this, Method.POST, path);
	}

	public BodyfulRequestBuilder PUT(String path) {
		return new BodyfulRequestBuilder(this, Method.PUT, path);
	}

	public BodyfulRequestBuilder PATCH(String path) {
		return new BodyfulRequestBuilder(this, Method.PATCH, path);
	}

	@Override
	public String toString() {
		return "HttlSender [" + config.getUrl() + ", executor=" + executor + "]";
	}

	/*
		public static class HttlHeaders extends Multival {

			private static final long serialVersionUID = 1L;

			public HttlHeaders() {

			}
		}
	
	public static class Parameters extends Multival {

		private static final long serialVersionUID = 1L;

	}
	*/
	/**
	 * 
	 * @author martin.vanek
	 *
	 */
	public static class Multival implements Iterable, Serializable {

		private static final long serialVersionUID = 1L;

		private Map> entries;

		public Multival() {
		}

		/**
		 * Set value(s) replacing existing
		 * 
		 */
		public void set(String name, Collection values) {
			if (values != null && values.size() != 0) {
				List list = get(name, true);
				for (T value : values) {
					list.add(value);
				}
			} else {
				get(name, true);//.add(null);
			}
		}

		/**
		 * Set value(s) replacing existing
		 * 
		 */
		public void set(String name, T... values) {
			if (values != null && values.length != 0) {
				List list = get(name, true);
				for (T value : values) {
					list.add(value);
				}
			} else {
				get(name, true);//.add(null);
			}

		}

		/**
		 * Set value(s) replacing existing
		 * 
		 */
		public void set(String name, T value) {
			get(name, true).add(value);
		}

		public List remove(String name) {
			return this.entries.remove(name);
		}

		/**
		 * Add value(s) keeping existing
		 */
		public void add(String name, Collection values) {
			if (values != null && values.size() != 0) {
				List list = get(name, false);
				for (T value : values) {
					list.add(value);
				}
			} else {
				get(name, false);//.add(null);
			}
		}

		/**
		 * Add value(s) keeping existing
		 */
		public void add(String name, T... values) {
			if (values != null && values.length != 0) {
				List list = get(name, false);
				for (T value : values) {
					list.add(value);
				}
			} else {
				get(name, false);//.add(null);
			}
		}

		/**
		 * Add value(s) keeping existing
		 */
		public void add(String name, T value) {
			get(name, false).add(value);
		}

		public void put(String name, T value, boolean clear) {
			get(name, clear).add(value);
		}

		protected List get(String name, boolean clear) {
			if (Cutils.isBlank(name)) {
				throw new IllegalArgumentException("Name is blank");
			}
			List list = null;
			if (this.entries == null) {
				this.entries = new TreeMap>();
			} else {
				list = this.entries.get(name);
			}
			if (list == null) {
				list = new LinkedList();
				this.entries.put(name, list);
			} else if (clear) {
				list.clear();
			}
			return list;

		}

		public Set names() {
			if (this.entries == null) {
				return Collections.emptySet();
			} else {
				return this.entries.keySet();
			}
		}

		public int size() {
			return this.entries == null ? 0 : this.entries.size();
		}

		public List get(String name) {
			if (this.entries == null) {
				return null;
			} else {
				return this.entries.get(name);
			}
		}

		public T getFirst(String name) {
			List values = get(name);
			if (values != null && values.size() != 0) {
				return values.get(0);
			} else {
				return null;
			}
		}

		public T getLast(String name) {
			List values = get(name);
			if (values != null && values.size() != 0) {
				return values.get(values.size() - 1);
			} else {
				return null;
			}
		}

		public void clear() {
			if (this.entries != null) {
				entries.clear();
			}
		}

		@Override
		public Iterator iterator() {
			if (this.entries == null) {
				return Collections. emptySet().iterator();
			}
			return names().iterator();
		}

		@Override
		public String toString() {
			return String.valueOf(this.entries);
		}

		@Override
		public int hashCode() {
			return this.entries == null ? 0 : this.entries.hashCode();
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj) {
				return true;
			}
			if (obj == null) {
				return false;
			}
			if (getClass() != obj.getClass()) {
				return false;
			}
			Multival other = (Multival) obj;
			if (this.entries == null) {
				if (other.entries != null) {
					return false;
				}
			} else if (!this.entries.equals(other.entries)) {
				return false;
			}
			return true;
		}

		@Override
		public Multival clone() {
			Multival multival = new Multival();
			multival.entries = new HashMap>();
			for (Entry> entry : entries.entrySet()) {
				multival.entries.put(entry.getKey(), entry.getValue());
			}
			return multival;
		}
	}

	private static final Comparator COMPARATOR = new NullSafeStringComparator();

	private static class NullSafeStringComparator implements Comparator {

		@Override
		public int compare(String o1, String o2) {
			if (o1 == o2) {
				return 0;
			} else if (o1 == null) {
				return -1;
			} else if (o2 == null) {
				return 1;
			} else {
				return o1.compareTo(o2);
			}
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy