-
Notifications
You must be signed in to change notification settings - Fork 140
[DOC] Intercepting method calls, grabbing http request, using BeforeMethodCall, AOP like services with qbit
Recently someone asked me if you could capture the request parameters from a request with QBit REST support. You can.
QBit has this interface.
package io.advantageous.qbit.service;
import io.advantageous.qbit.message.MethodCall;
/**
* Use this to register for before method calls for services.
* <p>
* created by Richard on 8/26/14.
*
* @author rhightower
*/
public interface BeforeMethodCall {
boolean before(MethodCall call);
}
With this BeforeMethodCall
interface you can intercept a method call. If you register it with a ServiceQueue
via the ServiceBuilder
then the method interception happens in the same thread as the service queue method calls.
If you return false from the before method then the call will not be made. You can also intercept calls at the ServiceBundle
and the ServiceEndpointServer
levels using the EndpointServerBuilder
and the ServiceBundleBuilder
. When you register a BeforeMethodCall
with a service bundle or and server end point, it gets called before the method is enqueued to the actual service queue. When you register a BeforeMethodCall
with a service queue, it gets called right before the method gets invoked in the same thread as the service queue, i.e., in the service thread, which is most useful for capturing the HttpRequest
.
But let's say that you want to access the HttpRequest
object to do something special with it. Perhaps read the request params.
This is possible. One merely has to intercept the call. Every Request
object has a property called originatingRequest
. A MethodCall
is a Request
object as is an HttpRequest
. This means that you just have to intercept the call with BeforeMethodCall
grab the methodCall
, and then use it to get the HttpRequest
.
/**
* Created by rhightower.
*/
@RequestMapping("/api")
public class PushService {
private final ThreadLocal<HttpRequest> currentRequest;
public PushService() {
this.currentRequest = new ThreadLocal<>();
}
@RequestMapping(value = "/event", method = RequestMethod.POST)
public void event(final Callback<Boolean> callback, final Event event) {
final HttpRequest httpRequest = currentRequest.get();
System.out.println(httpRequest.address());
System.out.println(httpRequest.params().size());
...
}
Now in the main method, we will need to construct the service and then register the service with the endpoint.
Notice the private final ThreadLocal<HttpRequest> currentRequest;
because we will use that to store the current http request.
final ManagedServiceBuilder managedServiceBuilder = ManagedServiceBuilder.managedServiceBuilder();
final PushService pushService = new PushService();
final ServiceEndpointServer serviceEndpointServer =
managedServiceBuilder.getEndpointServerBuilder()
.setUri("/")
.build();
...
final ServiceQueue pushServiceQueue = ...
serviceEndpointServer.addServiceQueue("/api/pushservice", pushServiceQueue);
Notice when we create the service queue separately we have to register the address it is bound under.
When we create the service queue (pushServiceQueue
) for pushService
, we want to tell it to use the same response queue as our endpoint server and register the beforeCall lambda to capture the HttpRequest
from the MethodCall
.
Creating a lambda expression to populate the currentRequest from the originatingRequest of the MethodCall (call)
final ServiceQueue pushServiceQueue = managedServiceBuilder
.createServiceBuilderForServiceObject(pushService)
.setResponseQueue(serviceEndpointServer.serviceBundle().responses())
.setBeforeMethodCall(call -> {
pushService.currentRequest.set((HttpRequest) call.originatingRequest());
return true;
})
.buildAndStart();
The full example is a bit longer as it has some other things not mentioned in this article.
public class Event {
private final String name;
public Event(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
....
import io.advantageous.qbit.annotation.*;
import io.advantageous.qbit.admin.ManagedServiceBuilder;
import io.advantageous.qbit.http.request.HttpRequest;
import io.advantageous.qbit.reactive.Callback;
import io.advantageous.qbit.reactive.Reactor;
import io.advantageous.qbit.reactive.ReactorBuilder;
import io.advantageous.qbit.server.ServiceEndpointServer;
import io.advantageous.qbit.service.ServiceQueue;
import java.util.concurrent.TimeUnit;
/**
* Created by rhightower.
*/
@RequestMapping("/api")
public class PushService {
private final Reactor reactor;
private final StoreServiceClient storeServiceClient;
private final ThreadLocal<HttpRequest> currentRequest;
public PushService(final Reactor reactor,
final StoreServiceClient storeServiceClient) {
this.reactor = reactor;
this.storeServiceClient = storeServiceClient;
this.currentRequest = new ThreadLocal<>();
}
@RequestMapping("/hi")
public String sayHi() {
return "hi";
}
@RequestMapping(value = "/event", method = RequestMethod.POST)
public void event(final Callback<Boolean> callback, final Event event) {
final HttpRequest httpRequest = currentRequest.get();
System.out.println(httpRequest.address());
System.out.println(httpRequest.params().baseMap());
storeServiceClient.addEvent(callback, event);
}
@QueueCallback({QueueCallbackType.LIMIT, QueueCallbackType.EMPTY, QueueCallbackType.IDLE})
public void load() {
reactor.process();
}
public static void main(String... args) {
/* Using new snapshot 2. */
final ManagedServiceBuilder managedServiceBuilder = ManagedServiceBuilder.managedServiceBuilder();
final StoreService storeService = new StoreService();
final ServiceQueue serviceQueue = managedServiceBuilder.createServiceBuilderForServiceObject(storeService)
.buildAndStartAll();
final StoreServiceClient storeServiceClient = serviceQueue.createProxyWithAutoFlush(StoreServiceClient.class,
100, TimeUnit.MILLISECONDS);
final PushService pushService = new PushService(ReactorBuilder.reactorBuilder().build(),
storeServiceClient);
final ServiceEndpointServer serviceEndpointServer = managedServiceBuilder.getEndpointServerBuilder()
.setUri("/")
.build();
final ServiceQueue pushServiceQueue = managedServiceBuilder
.createServiceBuilderForServiceObject(pushService)
.setResponseQueue(serviceEndpointServer.serviceBundle().responses())
.setBeforeMethodCall(call -> {
pushService.currentRequest.set((HttpRequest) call.originatingRequest());
return true;
})
.buildAndStart();
serviceEndpointServer.addServiceQueue("/api/pushservice", pushServiceQueue);
serviceEndpointServer.startServer();
/* Wait for the service to shutdown. */
managedServiceBuilder.getSystemManager().waitForShutdown();
}
}
...
public class StoreService {
public boolean addEvent(final Event event) {
return true;
}
}
...
import io.advantageous.qbit.reactive.Callback;
public interface StoreServiceClient {
void addEvent(final Callback<Boolean> callback, final Event event);
}
...
import io.advantageous.boon.json.JsonFactory;
import io.advantageous.qbit.http.HTTP;
import static io.advantageous.boon.core.IO.puts;
public class TestMain {
public static void main(final String... args) throws Exception {
HTTP.Response hello = HTTP.jsonRestCallViaPOST("http://localhost:9090/api/event", JsonFactory.toJson(new Event("hello")));
puts(hello.body(), hello.status());
}
}
There was a slight bug in the ServiceBuilder
. It was not passing the BeforeMethodCall
to the service queue. It has been remedied and will be in the 0.8.16-RELEASE.
Now you can take this example a step further.
Let's say you want to return something other than JSON. Perhaps, God forbid, XML.
You create a callback, but never call it.
Then you grab the request object. Output whatever you want with no worry about JSON encoding. Mark the request as handled, and you are off to the races.
@RequestMapping(value = "/event", method = RequestMethod.POST)
public void event(final Callback<Boolean> callback, final Event event) {
final HttpRequest httpRequest = currentRequest.get();
System.out.println(httpRequest.address());
System.out.println(httpRequest.params().baseMap());
//storeServiceClient.addEvent(callback, event);
httpRequest.getReceiver().respond(200, "blue"); //do whatever you want, it will not escape it further.
httpRequest.handled();
}
The key here is don’t call the callback. And remember to mark the request as handled, then you can do whatever you want.
Send whatever text that you want. In the case of SOLR query that get JSON from SOLR that you do not want to double JSON encode, it will not double encode SOLR JSON string.
QBit Website What is Microservices Architecture?
QBit Java Micorservices lib tutorials
The Java microservice lib. QBit is a reactive programming lib for building microservices - JSON, HTTP, WebSocket, and REST. QBit uses reactive programming to build elastic REST, and WebSockets based cloud friendly, web services. SOA evolved for mobile and cloud. ServiceDiscovery, Health, reactive StatService, events, Java idiomatic reactive programming for Microservices.
Reactive Programming, Java Microservices, Rick Hightower
Java Microservices Architecture
[Microservice Service Discovery with Consul] (http://www.mammatustech.com/Microservice-Service-Discovery-with-Consul)
Microservices Service Discovery Tutorial with Consul
[Reactive Microservices] (http://www.mammatustech.com/reactive-microservices)
[High Speed Microservices] (http://www.mammatustech.com/high-speed-microservices)
Reactive Microservices Tutorial, using the Reactor
QBit is mentioned in the Restlet blog
All code is written using JetBrains Idea - the best IDE ever!
Kafka training, Kafka consulting, Cassandra training, Cassandra consulting, Spark training, Spark consulting
Tutorials
- QBit tutorials
- Microservices Intro
- Microservice KPI Monitoring
- Microservice Batteries Included
- RESTful APIs
- QBit and Reakt Promises
- Resourceful REST
- Microservices Reactor
- Working with JSON maps and lists
__
Docs
Getting Started
- First REST Microservice
- REST Microservice Part 2
- ServiceQueue
- ServiceBundle
- ServiceEndpointServer
- REST with URI Params
- Simple Single Page App
Basics
- What is QBit?
- Detailed Overview of QBit
- High level overview
- Low-level HTTP and WebSocket
- Low level WebSocket
- HttpClient
- HTTP Request filter
- HTTP Proxy
- Queues and flushing
- Local Proxies
- ServiceQueue remote and local
- ManagedServiceBuilder, consul, StatsD, Swagger support
- Working with Service Pools
- Callback Builders
- Error Handling
- Health System
- Stats System
- Reactor callback coordination
- Early Service Examples
Concepts
REST
Callbacks and Reactor
Event Bus
Advanced
Integration
- Using QBit in Vert.x
- Reactor-Integrating with Cassandra
- Using QBit with Spring Boot
- SolrJ and service pools
- Swagger support
- MDC Support
- Reactive Streams
- Mesos, Docker, Heroku
- DNS SRV
QBit case studies
QBit 2 Roadmap
-- Related Projects
- QBit Reactive Microservices
- Reakt Reactive Java
- Reakt Guava Bridge
- QBit Extensions
- Reactive Microservices
Kafka training, Kafka consulting, Cassandra training, Cassandra consulting, Spark training, Spark consulting