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

aQute.openapi.provider.Dispatcher Maven / Gradle / Ivy

package aQute.openapi.provider;

import java.io.Closeable;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.osgi.service.http.NamespaceException;

import aQute.lib.strings.Strings;
import aQute.openapi.provider.OpenAPIRuntime.Tracker;
import aQute.openapi.security.api.OpenAPIAuthenticator;

public class Dispatcher extends HttpServlet {
	private static final int					SECURITY_PROVIDER_TIMEOUT	= 5000;
	private static final long					serialVersionUID			= 1L;
	final String								prefix;
	final OpenAPIRuntime						runtime;
	final List							targets						= new CopyOnWriteArrayList<>();
	final Map	security					= new ConcurrentHashMap<>();
	final Closeable								registration;
	final Object								lock						= new Object();
	final String								cacheControl;

	public Dispatcher(OpenAPIRuntime runtime, String prefix) throws ServletException, NamespaceException {
		this.runtime = runtime;
		this.prefix = prefix;

		if (runtime.configuration != null && runtime.configuration.cacheControl().length > 0) {
			cacheControl = Strings.join(runtime.configuration.cacheControl());
		} else
			cacheControl = null;

		assert this.prefix.startsWith("/");

		registration = runtime.registerServlet(this.prefix, this);
	}

	public void add(Tracker base) {
		synchronized (lock) {
			this.targets.add(base);
			lock.notifyAll();
		}
	}

	public void remove(Tracker base) {
		synchronized (lock) {
			this.targets.remove(base);
		}
	}

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		OpenAPIContext context = new OpenAPIContext(runtime, this, request, response);
		String encoderName = StandardCharsets.UTF_8.name();
		String path = getUnencodedPathInfo(request, encoderName);
		try {
			try {
				runtime.contexts.set(context);
				try {
					if (path.endsWith("/"))
						path = path.substring(1, path.length() - 1);
					else
						path = path.substring(1);

					String segments[] = path.split("/");
					for (int i = 0; i < segments.length; i++) {
						segments[i] = URLDecoder.decode(segments[i], encoderName);
					}

					int counter = runtime.delayOn404Timeout;
					do {
						for (Tracker target : targets) {
							OpenAPIBase base = target.base;
							context.setTarget(base);
							base.before_(context);
							try {
								doFinalHeaders(request, response);
								if (base.dispatch_(context, segments, 0)) {
									if (response.getContentType() == null)
										response.setContentType("application/json");

									return;
								}
							} finally {
								base.after_(context);
							}
						}

						if (counter-- > 0) {
							synchronized (lock) {
								lock.wait(1000);
							}
						} else {
							response.setStatus(HttpServletResponse.SC_NOT_FOUND);
							return;
						}
					} while (true);
				} finally {
					runtime.contexts.remove();
				}
			} catch (OpenAPIBase.Response e) {
				response.setStatus(e.resultCode);
				for (Entry entry : e.headers.entrySet()) {
					response.addHeader(entry.getKey(), entry.getValue());
				}
				Object result = e.getResult();
				context.report(e);
				doFinalHeaders(request, response);
			} catch (OpenAPIBase.DoNotTouchResponse e) {
				// do not touch response
				doFinalHeaders(request, response);
			} catch (SecurityException se) {
				OpenAPIRuntime.logger.warn("Forbidden {} {}", se, request.getPathInfo());
				response.setStatus(HttpServletResponse.SC_FORBIDDEN);
			} catch (Exception e) {
				OpenAPIRuntime.logger.warn("Server Error on " + request.getPathInfo(), e);
				context.report(e);
				if (runtime.security == null
						|| !runtime.security.handleException(e, context.getOperation(), request, response))
					response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			}
		} catch (Exception e) {
			OpenAPIRuntime.logger.error("Failure in post request processing", e);
		}
	}

	private void doFinalHeaders(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (runtime.cors != null) {
			runtime.cors.fixup(request, response);
		}
		if (cacheControl != null && response.getHeader("Cache-Control") == null) {
			response.setHeader("Cache-Control", cacheControl);
		}
	}

	private String getUnencodedPathInfo(HttpServletRequest request, String encoderName)
			throws UnsupportedEncodingException {
		String path = request.getRequestURI()
				.substring(request.getContextPath().length() + request.getServletPath().length());

		int pos = path.indexOf(';');
		if (pos != -1) {
			path = path.substring(0, pos);
		}
		return path;
	}

	@Override
	public String toString() {
		StringBuilder s = new StringBuilder();
		s.append(prefix);
		s.append(" - ").append(targets);
		return super.toString();
	}

	public void close() throws IOException {
		security.values().forEach(SecurityProviderTracker::close);
		registration.close();
	}

	public OpenAPIAuthenticator getSecurityProvider(String id, String type) throws InterruptedException {
		String key = id + "-" + type;
		SecurityProviderTracker t;

		synchronized (security) {
			t = security.get(key);
			if (t == null) {
				t = new SecurityProviderTracker(runtime.context, id, type);
				security.put(key, t);
				t.open();
			}
		}
		OpenAPIAuthenticator provider = t.waitForService(SECURITY_PROVIDER_TIMEOUT);
		return provider;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy