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.processing;
17  
18  import java.io.InputStream;
19  import java.io.PrintWriter;
20  
21  import jakarta.servlet.http.HttpServlet;
22  import jakarta.servlet.http.HttpServletRequest;
23  import jakarta.servlet.http.HttpServletResponse;
24  
25  import org.rribbit.ListenerObject;
26  import org.rribbit.Request;
27  import org.rribbit.Response;
28  import org.rribbit.dispatching.HttpRequestDispatcher;
29  import org.rribbit.execution.ListenerObjectExecutor;
30  import org.rribbit.retrieval.ListenerObjectRetriever;
31  import org.rribbit.util.Base64Util;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  /**
36   * This servlet processes requests that are dispatched by the {@link HttpRequestDispatcher}. It is not actually an implementation of {@link RequestProcessor}, but uses a nested
37   * {@link LocalRequestProcessor} to do the actual processing. This is not a problem though, since an {@link HttpRequestProcessorServlet} receives its requests via HTTP and not via
38   * a Java interface. It accepts a {@link ListenerObjectRetriever} and a {@link ListenerObjectExecutor}, just like any other {@link RequestProcessor}.
39   * <p />
40   * RRiBbit over HTTP uses POST exclusively. Other HTTP methods are NOT used.
41   * <p />
42   * This is an abstract class, because it needs a {@link ListenerObjectRetriever} and a {@link ListenerObjectExecutor} to do its work, just like any other {@link RequestProcessor}.
43   * These however, cannot be passed in the constructor, since passing objects in a constructor cannot be done with Java Servlets. The programmer must therefore subclass this class
44   * and provide a {@link ListenerObjectRetriever} and a {@link ListenerObjectExecutor} via the abstract methods.
45   * <p />
46   * If you use Spring, then the {@link SpringHttpRequestProcessorServlet} provides an implementation of this class that retrieves the {@link ListenerObjectRetriever} and the
47   * {@link ListenerObjectExecutor} from the WebApplicationContext.
48   * <p />
49   * An {@link HttpRequestProcessorServlet} can be mounted on an SSL or a non-SSL server, it should make no difference to its behaviour.
50   *
51   * @author G.J. Schouten
52   *
53   */
54  public abstract class HttpRequestProcessorServlet extends HttpServlet {
55  
56  	private static final Logger log = LoggerFactory.getLogger(HttpRequestProcessorServlet.class);
57  
58  	protected RequestProcessor requestProcessor;
59  
60  	@Override
61  	protected void doPost(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
62  
63  		log.info("Processing HTTP Request");
64  		try (InputStream requestInputStream = servletRequest.getInputStream()) {
65  			log.info("Decoding Request");
66  			Request request = Base64Util.decodeInputStream(requestInputStream);
67  
68  			log.info("Processing decoded Request");
69  			Response<?> response = requestProcessor.processRequest(request);
70  
71  			log.info("Encoding Response");
72  			String responseString = Base64Util.encodeObject(response);
73  
74  			log.info("Returning Response");
75  			servletResponse.setHeader("Content-Type", "text/plain");
76  			try (PrintWriter writer = servletResponse.getWriter()) {
77  				writer.write(responseString);
78  				writer.flush();
79  			}
80  		} catch (Exception e) {
81  			log.error("Error during processing of HTTP Request", e);
82  		}
83  	}
84  
85  	/**
86  	 * When you override this, please make sure to call super.init().
87  	 */
88  	@Override
89  	public void init() {
90  		requestProcessor = new LocalRequestProcessor(this.createListenerObjectRetriever(), this.createListenerObjectExecutor());
91  	}
92  
93  	/**
94  	 * Creates or fetches and returns a {@link ListenerObjectRetriever} that will be used to retrieve {@link ListenerObject}s.
95  	 *
96  	 * @return The {@link ListenerObjectRetriever} that will be used to retrieve {@link ListenerObject}s
97  	 */
98  	protected abstract ListenerObjectRetriever createListenerObjectRetriever();
99  
100 	/**
101 	 * Creates or fetches and returns a {@link ListenerObjectExecutor} that will be used to execute {@link ListenerObject}s.
102 	 *
103 	 * @return The {@link ListenerObjectExecutor} that will be used to execute {@link ListenerObject}s
104 	 */
105 	protected abstract ListenerObjectExecutor createListenerObjectExecutor();
106 }