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

org.refcodes.web.HttpClientRequest Maven / Gradle / Ivy

package org.refcodes.web;

import java.util.HashMap;
import java.util.Map;

import org.refcodes.exception.MarshalException;
import org.refcodes.struct.PathMap;
import org.refcodes.textual.VerboseTextBuilder;
import org.refcodes.web.HttpBodyAccessor.HttpBodyProvider;
import org.refcodes.web.RedirectDepthAccessor.RedirectDepthProperty;

/**
 * Defines a {@link HttpClientRequest} being the request as produced by the
 * client.
 */
public class HttpClientRequest extends AbstractHttpRequest implements HttpBodyProvider, RedirectDepthProperty {

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	public static final int DEFAULT_REDIRECT_DEPTH = -1; // RetryCount.NORM.getValue();

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	protected MediaTypeFactoryLookup _mediaTypeFactoryLookup;
	private int _redirectDepth = DEFAULT_REDIRECT_DEPTH;
	private Object _request = null;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Instantiates a new http client request impl.
	 *
	 * @param aHttpMethod the http method
	 * @param aUrl The {@link Url} from which to take the URL specific data.
	 * @param aMediaTypeFactoryLookup the media type factory lookup
	 */
	public HttpClientRequest( HttpMethod aHttpMethod, Url aUrl, MediaTypeFactoryLookup aMediaTypeFactoryLookup ) {
		this( aHttpMethod, aUrl, null, DEFAULT_REDIRECT_DEPTH, aMediaTypeFactoryLookup );
	}

	/**
	 * Instantiates a new http client request impl.
	 *
	 * @param aHttpMethod the http method
	 * @param aUrl The {@link Url} from which to take the URL specific data.
	 * @param aRequest the request
	 * @param aMediaTypeFactoryLookup the media type factory lookup
	 */
	public HttpClientRequest( HttpMethod aHttpMethod, Url aUrl, Object aRequest, MediaTypeFactoryLookup aMediaTypeFactoryLookup ) {
		this( aHttpMethod, aUrl, null, aRequest, DEFAULT_REDIRECT_DEPTH, aMediaTypeFactoryLookup );
	}

	/**
	 * Instantiates a new http client request impl.
	 *
	 * @param aHttpMethod the http method
	 * @param aUrl The {@link Url} from which to take the URL specific data.
	 * @param aHeaderFields the Header-Fields
	 * @param aRequest the request
	 * @param aMediaTypeFactoryLookup the media type factory lookup
	 */
	public HttpClientRequest( HttpMethod aHttpMethod, Url aUrl, RequestHeaderFields aHeaderFields, Object aRequest, MediaTypeFactoryLookup aMediaTypeFactoryLookup ) {
		this( aHttpMethod, aUrl, aHeaderFields, aRequest, DEFAULT_REDIRECT_DEPTH, aMediaTypeFactoryLookup );
	}

	/**
	 * Instantiates a new http client request impl.
	 *
	 * @param aHttpMethod the http method
	 * @param aUrl The {@link Url} from which to take the URL specific data.
	 * @param aRedirectDepth The redirect depth provides the count of
	 *        HTTP-Request and HTTP-Response cycles where the response
	 *        represents a redirect as of
	 *        {@link HttpStatusCode#isRedirectStatus()}. A value of -1
	 *        represents a nearly infinite HTTP-Request and HTTP-Response cycle
	 *        (infinite might mean a maximum of 256 cycle).
	 * @param aMediaTypeFactoryLookup the media type factory lookup
	 */
	public HttpClientRequest( HttpMethod aHttpMethod, Url aUrl, int aRedirectDepth, MediaTypeFactoryLookup aMediaTypeFactoryLookup ) {
		this( aHttpMethod, aUrl, null, aRedirectDepth, aMediaTypeFactoryLookup );
	}

	/**
	 * Instantiates a new http client request impl.
	 *
	 * @param aHttpMethod the http method
	 * @param aUrl The {@link Url} from which to take the URL specific data.
	 * @param aRequest the request
	 * @param aRedirectDepth The redirect depth provides the count of
	 *        HTTP-Request and HTTP-Response cycles where the response
	 *        represents a redirect as of
	 *        {@link HttpStatusCode#isRedirectStatus()}. A value of -1
	 *        represents a nearly infinite HTTP-Request and HTTP-Response cycle
	 *        (infinite might mean a maximum of 256 cycle).
	 * @param aMediaTypeFactoryLookup the media type factory lookup
	 */
	public HttpClientRequest( HttpMethod aHttpMethod, Url aUrl, Object aRequest, int aRedirectDepth, MediaTypeFactoryLookup aMediaTypeFactoryLookup ) {
		this( aHttpMethod, aUrl, null, aRequest, aRedirectDepth, aMediaTypeFactoryLookup );
	}

	/**
	 * Instantiates a new http client request impl.
	 *
	 * @param aHttpMethod the http method
	 * @param aUrl The {@link Url} from which to take the URL specific data.
	 * @param aHeaderFields the Header-Fields
	 * @param aRequest the request
	 * @param aRedirectDepth The redirect depth provides the count of
	 *        HTTP-Request and HTTP-Response cycles where the response
	 *        represents a redirect as of
	 *        {@link HttpStatusCode#isRedirectStatus()}. A value of -1 keeps a
	 *        redirect depth as used by default by underlying implementations.
	 * @param aMediaTypeFactoryLookup the media type factory lookup
	 */
	public HttpClientRequest( HttpMethod aHttpMethod, Url aUrl, RequestHeaderFields aHeaderFields, Object aRequest, int aRedirectDepth, MediaTypeFactoryLookup aMediaTypeFactoryLookup ) {
		super( aHttpMethod, aUrl, aHeaderFields );
		_mediaTypeFactoryLookup = aMediaTypeFactoryLookup;
		_redirectDepth = aRedirectDepth;
		setRequest( aRequest );
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Retrieves the request from the request property.
	 *
	 * @param  the generic type
	 * 
	 * @return The request stored by the request property.
	 */
	@SuppressWarnings("unchecked")
	public  REQ getRequest() {
		return (REQ) _request;
	}

	/**
	 * Sets the request for the request property. The
	 * {@link HttpClientRequest#setRequest(Object)} supports the
	 * {@link HttpBodyMap} to marshal an {@link HttpBodyMap} into an HTTP
	 * Request-Body.
	 *
	 * @param  the generic type
	 * @param aRequest The request to be stored by the request property.
	 */
	public  void setRequest( REQ aRequest ) {
		_request = aRequest;
		if ( aRequest != null && getHeaderFields() != null && getHeaderFields().getContentType() == null ) {
			getHeaderFields().putContentType( toPreferredContentType() );
		}
	}

	/**
	 * Automatically sets the {@link ContentType} to the
	 * {@link RequestHeaderFields} retrieved from {@link #getHeaderFields()}.
	 * (see
	 * {@link RequestHeaderFields#putContentType(org.refcodes.web.HttpMediaType)}
	 * )
	 * 
	 * {@inheritDoc}
	 */
	@Override
	public String toHttpBody() throws BadRequestException {
		if ( _request == null ) {
			return null;
		}
		Map theProperties = null;
		final ContentType theContentType = toPreferredContentType();
		final MediaTypeFactory theFactory = _mediaTypeFactoryLookup.toMediaTypeFactory( theContentType.getMediaType() );
		final String theCharset = theContentType.getCharsetParametrer();
		if ( theCharset != null ) {
			theProperties = new HashMap<>();
			theProperties.put( MediaTypeParameter.CHARSET.getName(), theCharset );
		}

		if ( theFactory == null ) {
			throw new BadRequestException( "No Media-Type factory found (added) for request's Media-Type <" + theContentType + "> (raw requested Media-Type is <" + getHeaderFields().get( HeaderField.CONTENT_TYPE ) + ">)" );
		}
		try {
			// HttpModyMap support |-->
			Object theRequest = _request;
			if ( _request instanceof PathMap ) {
				theRequest = ( (PathMap) _request ).toDataStructure();
			}
			// HttpModyMap support <--|
			return theFactory.toMarshaled( theRequest, theProperties );
		}
		catch ( MarshalException e ) {
			throw new BadRequestException( "Unable to marshal request <" + getRequest() + "> to request's Media-Type <" + new VerboseTextBuilder().toString( theFactory.getMediaTypes() ) + ">.", e );
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return getClass().getName() + " [httpMethod=" + _httpMethod + ", url=" + _url + "]";
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Determines the preferred Content-Type by evaluating the Content-Type
	 * Header-Field and the supported {@link MediaTypeFactory} instances and
	 * taking the request into account. Depending on the evaluation, a
	 * Content-Type might be set by this method if none has been set before.
	 * 
	 * @return The preferred {@link ContentType}.
	 */
	private ContentType toPreferredContentType() {
		ContentType theContentType = getHeaderFields().getContentType();
		if ( theContentType == null ) {

			// |--> Do we have Form-Fields? Use "x-www-form-urlencoded"!
			if ( _request instanceof FormFields && _mediaTypeFactoryLookup.hasMediaTypeFactory( MediaType.APPLICATION_X_WWW_FORM_URLENCODED ) ) {
				return MediaType.APPLICATION_X_WWW_FORM_URLENCODED.toContentType();
			}
			// Do we have Form-Fields? Use "x-www-form-urlencoded"! <--|

			final MediaType[] theMediaTypes = _mediaTypeFactoryLookup.getFactoryMediaTypes();
			if ( theMediaTypes != null && theMediaTypes.length != 0 ) {
				getHeaderFields().putContentType( theMediaTypes[0] );
				theContentType = new ContentType( theMediaTypes[0] );
			}
		}
		return theContentType;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getRedirectDepth() {
		return _redirectDepth;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setRedirectDepth( int aRedirectDepth ) {
		_redirectDepth = aRedirectDepth;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy