View Javadoc
1   /*
2    * Copyright (C) 2012-2025 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 org.rribbit.Request;
19  import org.rribbit.Response;
20  import org.rribbit.processing.HttpRequestProcessorServlet;
21  import org.rribbit.util.Base64Util;
22  import org.slf4j.Logger;
23  import org.slf4j.LoggerFactory;
24  
25  import java.io.ByteArrayInputStream;
26  import java.net.URI;
27  import java.net.http.HttpClient;
28  import java.net.http.HttpRequest;
29  import java.net.http.HttpRequest.BodyPublishers;
30  import java.net.http.HttpResponse;
31  import java.net.http.HttpResponse.BodyHandlers;
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  		try {
74  			log.info("Encoding Request");
75  			String requestString = Base64Util.encodeObject(request);
76  
77  			HttpRequest httpRequest = HttpRequest.newBuilder()
78  			  .uri(new URI(url))
79  			  .POST(BodyPublishers.ofString(requestString))
80  			  .build();
81  
82  			log.info("Dispatching encoded Request");
83  			try(HttpClient httpClient = HttpClient.newHttpClient()) {
84  				HttpResponse<String> httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString());
85  
86  				log.info("Decoding Response");
87  				Response<T> response = Base64Util.decodeInputStream(new ByteArrayInputStream(httpResponse.body().getBytes()));
88  
89  				log.info("Returning Response");
90  				return response;
91  			}
92  		} catch(Exception e) {
93  			log.error("Something went wrong during the Request. Below are some common errors that can occur in the stacktrace: ");
94  			log.error("\tStreamCorruptedException: Indicates that the client does not understand the response from the server. This may mean that the client gets " +
95  				"a 404 page back, which it cannot decode. Please see the client's logs to see the response that the client got from the server. Please " +
96  				"also check the URL that you have given the client.");
97  			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.");
98  
99  			throw new RuntimeException(e);
100 		}
101 	}
102 
103 	public String getUrl() {
104 		return url;
105 	}
106 
107 	public void setUrl(String url) {
108 		this.url = url;
109 	}
110 }