Middleware

The Middleware API allows developers to execute code before a request is processed. Based on the incoming request, you can run custom logic, return custom streams and files, modify responses, rewrite, redirect, add headers, and more. All that before returning a response.

Middleware Use Cases

Using Middleware in an OSGi Application

To use the Middleware in an OSGi application, you need to run the following bundles:

  • com.equo.middleware.api

  • com.equo.middleware.ee.provider

API

You can use the middleware API in your application by injecting it as an OSGi service. Resource handlers for custom schemes must be added before any browser is initialized. Schemes http and https support the addition of handlers at any moment of the program execution.

Here’s an example of how you could set up a resource handler using a bundle activator and OSGi:

import org.osgi.service.component.annotations.Component;
import com.equo.middleware.api.IMiddlewareService;

@Component
public class ContributionComponent {

	@Reference
	private IMiddlewareService middlewareService;

	@Activate
	public void configureSchemes() {
		middlewareService.addResourceHandler("customscheme", "", (request, headers) -> {
			// Modify response headers if needed
			// Build response for the given request
			// Return readable InputStream containing the response data
		});
		...
	}

	...

}

In the example above we can see all that’s needed to add a resource handler to your application. A resource handler is used by the middleware implementation to know which requests to intercept, it’s defined by the parameters of the addResourceHandler method. The first parameter of the API is the scheme of the URL you’ll be intercepting requests from. The second parameter is the domain of that URL, if empty or NULL your resource handler will be used for all domains in the scheme. The third parameter is an instance of an IResponseHandler, it will be used for every request done to the resource handler.

Scheme

You can use existing schemes such as http and https and you can also pass arbitrary schemes like customscheme used in the examples in this documentation page.

Domain

You can also intercept requests to arbitrary domains, if you wish to intercept requests to all domains in the defined scheme you can pass an empty string or a null reference in this parameter.

IResponseHandler

The IResponseHandler that you pass to the resource handler will be called in every request intercepted by the middleware. It can be defined with a lambda, but it has two methods: shouldProcessRequest(Request request) : boolean and getResponseData(Request request, Map<String, String> headers) : InputStream. The middleware first asks the response handler if it should handle the request (by default it handles all requests), and only if it returns true we try to get the response data for the request.

Request

The Request object represents a Request made by the browser, it contains the request headers, its target URL and its HTTP method.

Custom Streams and Files

You can read your responses directly from the filesystem or use resources that you bundled directly in your jars.

import org.osgi.service.component.annotations.Component;
import com.equo.middleware.api.IMiddlewareService;

@Component
public class ContributionComponent {

	@Reference
	private IMiddlewareService middlewareService;

	@Activate
	public void configureSchemes() {
		middlewareService.addResourceHandler("customscheme", "", (request, headers) -> {
			File htmlResource = new File("path/to/a/resource.html");
			if (htmlResource.exists()) {
				return new FileInputStream(htmlResource);
			}
			// If the file doesn't exist fall back to a default resource contained in our classpath.
			return getClass().getClassLoader().getResourceAsStream("default.html");
		});
		...
	}

	...

}

Dynamic Content

You can also create your responses on demand depending on the Request.

import org.osgi.service.component.annotations.Component;
import com.equo.middleware.api.IMiddlewareService;
import com.equo.middleware.api.handler.IResponseConstants;

@Component
public class ContributionComponent {

	@Reference
	private IMiddlewareService middlewareService;

	@Activate
	public void configureSchemes() {
		middlewareService.addResourceHandler("customscheme", "", (request, headers) -> {
			headers.put(IResponseConstants.CONTENT_TYPE_HEADER, "text/html");
			StringBuilder sb = new StringBuilder("<!doctype html><html><body>");
			if (request.getUrl().endsWith("welcome")) {
				sb.append("<div> An awesome welcome page! </div>");
			} else {
				sb.append("This page is not implemented yet.");
			}
			sb.append("</body></html>");
			return new ByteArrayInputStream(sb.toString().getBytes());
		});
		...
	}

	...

}

Blocking requests

You can block requests by default if you want complete control over the requests done in the browser.

import org.osgi.service.component.annotations.Component;
import com.equo.middleware.api.IMiddlewareService;

@Component
public class ContributionComponent {

	@Reference
	private IMiddlewareService middlewareService;

	@Activate
	public void setupMiddleware() {
		middlewareService.blockByDefault(true);
		middlewareService.addAllowed("domain.com", "www.domain.com", "sub.domain.com");
		...
	}

	...

}

Custom Handling of Bundle Resources (OSGi)

OSGi’s bundleentry and bundleresource, and Eclipse’s platform protocols are handled by default by the middleware provider, so you can load resource URLs from these protocols directly without writing any extra code.