View Javadoc
1   /*
2    * Copyright (C) 2012-2024 RRiBbit.org
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.rribbit.dispatching;
17  
18  import java.io.InputStream;
19  
20  import org.apache.http.HttpEntity;
21  import org.apache.http.HttpResponse;
22  import org.apache.http.client.methods.HttpPost;
23  import org.apache.http.entity.StringEntity;
24  import org.apache.http.impl.client.CloseableHttpClient;
25  import org.apache.http.impl.client.HttpClientBuilder;
26  import org.rribbit.Request;
27  import org.rribbit.Response;
28  import org.rribbit.processing.HttpRequestProcessorServlet;
29  import org.rribbit.util.Base64Util;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  /**
34   * This {@link RequestDispatcher} dispatches a {@link Request} to an {@link HttpRequestProcessorServlet} via HTTP. Failover and loadbalancing are not supported in this class,
35   * as opposed to the {@link RmiRequestDispatcher}. The reason for this is that failover and loadbalancing are typically done in another layer of the HTTP stack, which
36   * does not apply to RMI.
37   * <p />
38   * This class uses Apache HTTPClient to make the connection to the {@link HttpRequestProcessorServlet}. It uses the default functionality of Apache HTTPClient to do its work.
39   * If you want to use more advanced functionality of Apache HTTPClient to make the connection, then it should be fairly straightforward to extend this class or implement your
40   * own {@link RequestDispatcher} implementation with this class as an example.
41   *
42   * @author G.J. Schouten
43   *
44   */
45  public class HttpRequestDispatcher implements RequestDispatcher {
46  
47  	private static final Logger log = LoggerFactory.getLogger(HttpRequestDispatcher.class);
48  
49  	protected String url;
50  
51  	/**
52  	 * Whenever you use this constructor, be sure to set the url with the setter provided by this class. If you don't, runtime exceptions will occur.
53  	 */
54  	public HttpRequestDispatcher() {}
55  
56  	/**
57  	 * Creates an {@link HttpRequestDispatcher} that will send the {@link Request}s to the specified URL. An {@link HttpRequestProcessorServlet} is expected to be listening
58  	 * at this URL. The URL is a standard HTTP URL, such as "http://host:port/path". It can also be an HTTPS URL, beginning with 'https:'.
59  	 *
60  	 * This constructor is recommended, since it forces you to specify the url. Passing a null value for this will result in runtime exceptions whenever
61  	 * the {@link HttpRequestDispatcher} is used.
62  	 *
63  	 * @param url
64  	 */
65  	public HttpRequestDispatcher(String url) {
66  		this.url = url;
67  	}
68  
69  	@Override
70  	public <T> Response<T> dispatchRequest(Request request) {
71  
72  		log.info("Dispatching HTTP Request");
73  		HttpPost httpPost = null;
74  		try {
75  			log.info("Encoding Request");
76  			String requestString = Base64Util.encodeObject(request);
77  
78  			log.info("Dispatching encoded Request");
79  			try(CloseableHttpClient httpclient = HttpClientBuilder.create().build()) {
80  				httpPost = new HttpPost(url);
81  				httpPost.setEntity(new StringEntity(requestString));
82  				HttpResponse httpResponse = httpclient.execute(httpPost);
83  				HttpEntity entity = httpResponse.getEntity();
84  
85  				try(InputStream inputStream = entity.getContent()) {
86  					log.info("Decoding Response");
87  					Response<T> response = Base64Util.decodeInputStream(inputStream);
88  
89  					log.info("Returning Response");
90  					return response;
91  				}
92  			}
93  		} catch(Exception e) {
94  			log.error("Something went wrong during the Request. Below are some common errors that can occur in the stacktrace: ");
95  			log.error("\tStreamCorruptedException: Indicates that the client does not understand the response from the server. This may mean that the client gets " +
96  				"a 404 page back, which it cannot decode. Please see the client's org.apache.http logs to see the response that the client got from the server. Please " +
97  				"also check the URL that you have given the client.");
98  			log.error("\tEOFException: Indicates that the client does not get a response from the server. Please check the server logs to see what's wrong.");
99  
100 			throw new RuntimeException(e);
101 		} finally {
102 			if(httpPost != null) {
103 				httpPost.releaseConnection();
104 			}
105 		}
106 	}
107 
108 	public String getUrl() {
109 		return url;
110 	}
111 
112 	public void setUrl(String url) {
113 		this.url = url;
114 	}
115 }