HttpRequestProcessorServlet.java

/*
 * Copyright (C) 2012-2024 RRiBbit.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.rribbit.processing;

import java.io.InputStream;
import java.io.PrintWriter;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.rribbit.ListenerObject;
import org.rribbit.Request;
import org.rribbit.Response;
import org.rribbit.dispatching.HttpRequestDispatcher;
import org.rribbit.execution.ListenerObjectExecutor;
import org.rribbit.retrieval.ListenerObjectRetriever;
import org.rribbit.util.Base64Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This servlet processes requests that are dispatched by the {@link HttpRequestDispatcher}. It is not actually an implementation of {@link RequestProcessor}, but uses a nested
 * {@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
 * a Java interface. It accepts a {@link ListenerObjectRetriever} and a {@link ListenerObjectExecutor}, just like any other {@link RequestProcessor}.
 * <p />
 * RRiBbit over HTTP uses POST exclusively. Other HTTP methods are NOT used.
 * <p />
 * 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}.
 * 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
 * and provide a {@link ListenerObjectRetriever} and a {@link ListenerObjectExecutor} via the abstract methods.
 * <p />
 * If you use Spring, then the {@link SpringHttpRequestProcessorServlet} provides an implementation of this class that retrieves the {@link ListenerObjectRetriever} and the
 * {@link ListenerObjectExecutor} from the WebApplicationContext.
 * <p />
 * An {@link HttpRequestProcessorServlet} can be mounted on an SSL or a non-SSL server, it should make no difference to its behaviour.
 *
 * @author G.J. Schouten
 *
 */
public abstract class HttpRequestProcessorServlet extends HttpServlet {

	private static final Logger log = LoggerFactory.getLogger(HttpRequestProcessorServlet.class);

	protected RequestProcessor requestProcessor;

	@Override
	protected void doPost(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

		log.info("Processing HTTP Request");
		try (InputStream requestInputStream = servletRequest.getInputStream()) {
			log.info("Decoding Request");
			Request request = Base64Util.decodeInputStream(requestInputStream);

			log.info("Processing decoded Request");
			Response<?> response = requestProcessor.processRequest(request);

			log.info("Encoding Response");
			String responseString = Base64Util.encodeObject(response);

			log.info("Returning Response");
			servletResponse.setHeader("Content-Type", "text/plain");
			try (PrintWriter writer = servletResponse.getWriter()) {
				writer.write(responseString);
				writer.flush();
			}
		} catch (Exception e) {
			log.error("Error during processing of HTTP Request", e);
		}
	}

	/**
	 * When you override this, please make sure to call super.init().
	 */
	@Override
	public void init() {
		requestProcessor = new LocalRequestProcessor(this.createListenerObjectRetriever(), this.createListenerObjectExecutor());
	}

	/**
	 * Creates or fetches and returns a {@link ListenerObjectRetriever} that will be used to retrieve {@link ListenerObject}s.
	 *
	 * @return The {@link ListenerObjectRetriever} that will be used to retrieve {@link ListenerObject}s
	 */
	protected abstract ListenerObjectRetriever createListenerObjectRetriever();

	/**
	 * Creates or fetches and returns a {@link ListenerObjectExecutor} that will be used to execute {@link ListenerObject}s.
	 *
	 * @return The {@link ListenerObjectExecutor} that will be used to execute {@link ListenerObject}s
	 */
	protected abstract ListenerObjectExecutor createListenerObjectExecutor();
}