Quantcast
Channel: Sleepless Dev
Viewing all articles
Browse latest Browse all 217

Using the QBit microservice lib with Async Servlets

$
0
0
You can now use QBit inside of any servlet engine. QBit supports Servlet Async API (early) as well as Vertx. 
You can run QBit standalone with Vertx (and soon Jetty) or you can embed QBit inside of any Servlet container.  (Next up: Servlet WebSocket support for QBit).

Qbit servlet support also a preview of using jetty and the QBit microservice lib together

I have a prototype that uses Jetty to prove out that qbit and Servlets is working for REST support.
Simple Service

Simple Service and Service Server construction

packageio.advantageous.qbit.servlet.integrationproto;
importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.http.HttpServer;
importio.advantageous.qbit.server.ServiceServer;
import staticio.advantageous.qbit.server.ServiceServerBuilder.serviceServerBuilder;

/**
* Created by rhightower on 2/12/15.
*/
publicclassMyServiceModule {
@RequestMapping("/ping")
publicstaticclassPingService {

@RequestMapping("/ping")
publicStringping() {
return"ok";
}
}

publicstaticServiceServerconfigureApp(finalHttpServerserver) {
return serviceServerBuilder().setHttpServer(server)
.build().initServices(newPingService()).startServer();
}
}
The above by design has nothing to do with Jetty or Servlets.
Then to bind it into a Servlet engine for testing, I use Jetty because Jetty is easy to use and embed.

Using Jetty to test QBit servlet support

packageio.advantageous.qbit.servlet.integrationproto;
importio.advantageous.qbit.http.HttpServer;
importio.advantageous.qbit.server.ServiceServer;
importio.advantageous.qbit.servlet.QBitHttpServlet;
importorg.eclipse.jetty.server.Server;
importorg.eclipse.jetty.servlet.ServletContextHandler;
importjavax.servlet.ServletConfig;

/**
* Created by rhightower on 2/12/15.
*/
publicclassQBitPlusJettyPrototype {

publicstaticclassMyQBitServletextendsQBitHttpServlet {
publicstaticfinalStringQBIT_SERVICE_SERVER_SERVER="QBit.ServiceServer.server";
privateServletConfig config;

@Override
protectedvoidwireHttpServer(finalHttpServerhttpServer, finalServletConfigconfig) {
finalServiceServer server =MyServiceModule.configureApp(httpServer);
config.getServletContext().setAttribute(QBIT_SERVICE_SERVER_SERVER, server);
this.config = config;
}

@Override
protectedvoidstop() {
finalServiceServer server =
(ServiceServer)
config.getServletContext().getAttribute(QBIT_SERVICE_SERVER_SERVER);
server.stop();
}
}

publicstaticvoidmain(String... args) throwsException {
Server server =newServer(8080);
ServletContextHandler servletContextHandler =newServletContextHandler(server,
"*", true, false);
servletContextHandler.addServlet(MyQBitServlet.class, "/services/*");
server.start();
server.join();
}
}
We just start up Jetty. Tell Jetty that it is a Servlet container. We create a servlet that extends QBitHttpServlet (that is the cross platform way to use QBit in any container, yes.. you could use QBit in Tomcat or Undertow or... God Forbid Websphere or WebLogic).
The support classes for QBit / Servlet support are:
  • QBitHttpServlet template design pattern to hook into Servlet lifecycle
  • QBitServletUtil utility to convert from HttpServletRequest to QBit HttpRequest
  • HttpServletParamMultiMap adapter to a multi-map from a servlet request
  • HttpServletHeaderMultiMap ditto but for headers (painful but lightweight)
  • ServletHttpServer lightweight HttpServer (this could be in core, it is wafer thin)

Implementation (Early)

I want to put this in qbit-core. It has no ties to Servlets at all.

ServletHttpServer

packageio.advantageous.qbit.servlet;

importio.advantageous.qbit.http.HttpRequest;
importio.advantageous.qbit.http.HttpServer;
importio.advantageous.qbit.http.WebSocketMessage;

importjava.util.concurrent.*;
importjava.util.function.Consumer;
importjava.util.function.Predicate;

/**
* Created by rhightower on 2/12/15.
*/
publicclassServletHttpServerimplementsHttpServer {

privateConsumer<WebSocketMessage> webSocketMessageConsumer = webSocketMessage -> {

};
privateConsumer<WebSocketMessage> webSocketCloseMessageConsumer = webSocketMessage -> {

};
privateConsumer<HttpRequest> httpRequestConsumer = request -> {

};
privateConsumer<Void> requestIdleConsumer = aVoid -> {};
privateConsumer<Void> webSocketIdleConsumer = aVoid -> {};
privatePredicate<HttpRequest> shouldContinueHttpRequest = request ->true;
privateScheduledFuture<?> future;

@Override
publicvoidsetShouldContinueHttpRequest(Predicate<HttpRequest>predicate) {
this.shouldContinueHttpRequest = predicate;
}

publicvoidhandleRequest(finalHttpRequestrequest) {
if (shouldContinueHttpRequest.test(request)) {
httpRequestConsumer.accept(request);
}
}

@Override
publicvoidsetWebSocketMessageConsumer(finalConsumer<WebSocketMessage>webSocketMessageConsumer) {
this.webSocketMessageConsumer = webSocketMessageConsumer;
}

@Override
publicvoidsetWebSocketCloseConsumer(Consumer<WebSocketMessage>webSocketCloseMessageConsumer) {
this.webSocketCloseMessageConsumer = webSocketCloseMessageConsumer;
}

@Override
publicvoidsetHttpRequestConsumer(Consumer<HttpRequest>httpRequestConsumer) {
this.httpRequestConsumer = httpRequestConsumer;
}

@Override
publicvoidsetHttpRequestsIdleConsumer(Consumer<Void>idleConsumer) {
this.requestIdleConsumer = idleConsumer;
}

@Override
publicvoidsetWebSocketIdleConsume(Consumer<Void>idleConsumer) {

this.webSocketIdleConsumer = idleConsumer;
}

privateScheduledExecutorService monitor;

@Override
publicvoidstart() {


monitor =Executors.newScheduledThreadPool(1,
runnable -> {
Thread thread =newThread(runnable);
thread.setName("ServletHttpServer Flush Thread" );
return thread;
}
);

/** This wants to be configurable. */
future = monitor.scheduleAtFixedRate(() -> {
try {
requestIdleConsumer.accept(null);
webSocketIdleConsumer.accept(null);
} catch (Exception ex) {
ex.printStackTrace();
//logger.error("blah blah Manager::Problem running queue manager", ex); //TODO log this
}
}, 50, 50, TimeUnit.MILLISECONDS);


}

@Override
publicvoidstop() {

if (future!=null) {
future.cancel(true);
}

if (monitor!=null) {
monitor.shutdown();
}
}
}
A Servlet so someone using Servlets can easily use QBit.

QBitHttpServlet - allows QBit REST to be easily used from Servlets

packageio.advantageous.qbit.servlet;

importio.advantageous.qbit.http.HttpRequest;
importio.advantageous.qbit.http.HttpServer;

importjavax.servlet.ServletConfig;
importjavax.servlet.ServletException;
importjavax.servlet.annotation.WebServlet;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjava.io.IOException;

import staticio.advantageous.qbit.servlet.QBitServletUtil.convertRequest;

/**
* Created by rhightower on 2/12/15.
*/
@WebServlet(asyncSupported =true)
publicabstractclassQBitHttpServletextendsHttpServlet {

privatefinalServletHttpServer httpServer =newServletHttpServer();

@Override
publicvoiddestroy() {
httpServer.stop();
stop();
}

protectedabstractvoidstop();

@Override
publicvoidinit(ServletConfigconfig) throwsServletException {
httpServer.start();
wireHttpServer(httpServer, config);
}

protectedabstractvoidwireHttpServer(finalHttpServerhttpServer, ServletConfigconfig);

@Override
protectedvoidservice(finalHttpServletRequestrequest, finalHttpServletResponseresponse)
throwsServletException, IOException {
finalHttpRequest httpRequest = convertRequest(request.startAsync());
httpServer.handleRequest(httpRequest);
}
}
Now the meat. This does all the work and glue. This glues the Servlet world to the QBit world.

QBitServletUtil GLUE

packageio.advantageous.qbit.servlet;

importio.advantageous.qbit.http.HttpRequest;
importio.advantageous.qbit.http.HttpRequestBuilder;
importio.advantageous.qbit.util.MultiMap;
importorg.boon.IO;

importjavax.servlet.AsyncContext;
importjavax.servlet.ServletInputStream;
importjavax.servlet.ServletOutputStream;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;

importjava.io.IOException;
importjava.nio.charset.StandardCharsets;

import staticio.advantageous.qbit.http.HttpRequestBuilder.httpRequestBuilder;

/**
* Created by rhightower on 2/12/15.
*/
publicclassQBitServletUtil {

publicstaticHttpRequestconvertRequest(finalAsyncContextasyncContext) {

finalHttpServletRequest request = (HttpServletRequest) asyncContext.getRequest();
finalHttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
finalMultiMap<String, String> headers =newHttpServletHeaderMultiMap(request);
finalMultiMap<String, String> params =newHttpServletParamMultiMap(request);
finalHttpRequestBuilder httpRequestBuilder =
httpRequestBuilder().setParams(params)
.setHeaders(headers).setUri(request.getRequestURI())
.setMethod(request.getMethod());

setRequestBodyIfNeeded(request, httpRequestBuilder);
setupRequestHandler(asyncContext, response, httpRequestBuilder);
return httpRequestBuilder.build();
}

privatestaticvoidsetupRequestHandler(AsyncContextasyncContext, HttpServletResponseresponse, HttpRequestBuilderhttpRequestBuilder) {
httpRequestBuilder.setTextResponse((code, contentType, body) -> {
response.setHeader("Content-Type", contentType);
try {
finalServletOutputStream outputStream = response.getOutputStream();
IO.write(outputStream, body);
outputStream.close();
asyncContext.complete();
} catch (IOException e) {
thrownewIllegalStateException(e);
}
});
}

privatestaticvoidsetRequestBodyIfNeeded(HttpServletRequestrequest, HttpRequestBuilderhttpRequestBuilder) {
if (request.getMethod().equals("POST") || request.getMethod().equals("PUT")) {
finalString body = readBody(request);
if (body!=null) {
httpRequestBuilder.setBody(body);
}
}
}

privatestaticStringreadBody(HttpServletRequestrequest) {
try {
finalServletInputStream inputStream = request.getInputStream();
finalString body =IO.read(inputStream, StandardCharsets.UTF_8);
return body;
} catch (IOException e) {
thrownewIllegalStateException(e);
}
}
}

Learn more about QBit:




  • [Detailed Tutorial] QBit microservice example
  • [Doc] Queue Callbacks for QBit queue based services
  • [Quick Start] Building a simple Rest web microservice server with QBit
  • [Quick Start] Building a TODO web microservice client with QBit
  • [Quick Start] Building a TODO web microservice server with QBit
  • [Quick Start] Building boon for the QBit microservice engine
  • [Quick Start] Building QBit the microservice lib for Java
  • [Rough Cut] Delivering up Single Page Applications from QBit Java JSON Microservice lib
  • [Rough Cut] Working with event bus for QBit the microservice engine
  • [Rough Cut] Working with inproc MicroServices
  • [Rough Cut] Working with private event bus for inproc microservices
  • [Rough Cut] Working with strongly typed event bus proxies for QBit Java Microservice lib
  • [Rough Cut] Working with System Manager for QBit Mircoservice lib
  • [Z Blog] Qbit servlet support also a preview of using jetty and the QBit microservice lib together
  • [Z Notebook] More benchmarking internal



  • Viewing all articles
    Browse latest Browse all 217

    Trending Articles