Refactor Legacy SOA system to use non-blocking async microservices - java

I've just started trying to get my head around RxJava so that I can use project reactor refactor Legacy SOA system to use non-blocking async microservices.
At the moment I'm do a feasibility study and looking at using something like spoon to transform the legacy service code (that has nothing to do with this question however)
I would like to know how I would use reactor-bus Request/Reply syntax to replace this synchronous service code. Or even if I should be using a completely different reactor construct.
Here is example of a legacy soa service, it is contrived so it might not make perfect sense, but basically each services is dependant on the results of the last.
public static Map<String, Object> createAccount(DispatchContext dctx, Map<String, Object> context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
String accountPartyId = (String) context.get("partyId");
Map<String, Object> input = UtilMisc.toMap("groupName", context.get("accountName"), "groupNameLocal", context.get("groupNameLocal"), "officeSiteName", context.get("officeSiteName"), "description", context.get("description"), "partyId", accountPartyId);
Map<String, Object> serviceResults1 = dispatcher.runSync("createPartyGroup", input);
Map<String, Object> serviceResults2 = dispatcher.runSync("createPartyRole", UtilMisc.toMap("partyId", (String) serviceResults1.get("partyId"), "roleTypeId", "ACCOUNT"));
String dataSourceId = (String) context.get("dataSourceId");
Map<String, Object> serviceResults3 = null;
if (dataSourceId != null) {
serviceResults3 = dispatcher.runSync("crmsfa.addAccountDataSource", UtilMisc.toMap("partyId", (String) serviceResults2.get("partyId"), "dataSourceId", dataSourceId));
}
String marketingCampaignId = (String) context.get("marketingCampaignId");
Map<String, Object> serviceResults4 = null;
if (marketingCampaignId != null) {
serviceResults4 = dispatcher.runSync("crmsfa.addAccountMarketingCampaign", UtilMisc.toMap("partyId", (String) serviceResults3.get("partyId"), "marketingCampaignId", marketingCampaignId));
}
String initialTeamPartyId = (String) context.get("initialTeamPartyId");
Map<String, Object> serviceResults5 = null;
if (initialTeamPartyId != null) {
serviceResults5 = dispatcher.runSync("crmsfa.assignTeamToAccount", UtilMisc.toMap("accountPartyId", (String) serviceResults4.get("partyId"), "teamPartyId", initialTeamPartyId, "userLogin", userLogin));
}
Map<String, Object> results = ServiceUtil.returnSuccess();
results.put("groupId", (String) serviceResults1.get("groupId"));
results.put("roleId", (String) serviceResults2.get("roleId"));
results.put("dataSourceId", (String) serviceResults3.get("dataSourceId"));
results.put("marketingCampaignId", (String) serviceResults4.get("marketingCampaignId"));
results.put("teamPartyId", (String) serviceResults5.get("teamPartyId"));
return results;
}
Basically this is a service that calls other services using dispatcher.runSync ... I'm just looking for a starting point for my research into how to possibly use reactor or even another library to transform this type of syntax into asynchronous non-blocking code.
At this point I'm thinking in very vague terms of callbacks/some sort of Promise type structure.
Like the first call to another service is
Map<String, Object> serviceResults = dispatcher.runSync("createPartyGroup", input);
If this instead returned a Promise object that contained the serviceResults map then the rest of the method could be moved into the Promise onComplete block and the result would be a deeply nested bunch of onComplete code blocks making up this service method.
Promise p = task {
// createPartyGroup service call
}
p.onComplete { result ->
Promise p2 = task {
// createPartyRole sevice call
}
p2.onComplete { result ->
//next service call
}
}
}
Or looking at reactor-bus documentation something like the following which doesn't make sense on many levels and I just don't know enough about reactor to know why it doesn't make sense or what to learn next for me to understand why it doesn't make sense
bus.send("service.createPartyGroup", Event.wrap(input, "reply.service.createPartyGroup"));
bus.receive($("reply.service.createPartyGroup"), ev -> {
Map<?> input2 = UtilMisc.toMap("partyId", (String) ev.get("partyId"), "roleTypeId", "ACCOUNT")
bus.send("service.createPartyRole", Event.wrap(input2, "reply.service.createPartyRole"));
});
I realise it is a rather odd place to start researching the reactive programming paradigm. But replacing this synchronous service code is my ultimate objective and if I understood at least the syntax I can work backwards from that.

You just need to use Observable, where in your flow one emitted item is passed through the stream.
Check the documentation https://github.com/ReactiveX/RxJava
This will be a sequential flow
Observable.just(methodThatCallFirstServiceAndReturnObservable(params))
.flatMap(resul1 -> methodThatCallSecondAndReturnObservable(resul1))
.flatMap(resul2 -> methodThatCallThirdAndReturnObservable(resul2))
.subscribe(result3->"Last value emmited here:");
You can run the three service call in parallel and get all the values together using Observable.zip or merge. But I believe is not what you need here.

Related

How to create reusable optional mapping with method references?

While I'm trying to use Optional features with method references, it really confused me how to optimize it with reusable code. I think I'm stuck while trying to use all those new features (for me) at the same time i decided to get rid of java-6 style, now I think i can't think simple, i feel that it gets overcomplicated. How can i create
List<BooleanExpression> expressionMapping = new ArrayList<>();
if (request != null) { // request is input parameter, a DTO
Optional.ofNullable(request.getPlantId())
.map(campaign.plant.id::contains) // campaign is static created by Querydsl
.ifPresent(expressionMapping::add);
Optional.ofNullable(request.getTitle())
.map(campaign.title::containsIgnoreCase)
.ifPresent(expressionMapping::add);
Optional.ofNullable(request.getCampaignNumber())
.map(this::getLikeWrapped)
.map(campaign.campaignNumber::like)
.ifPresent(expressionMapping::add);
... 20 more Optional bunch of code like this
}
also having trouble with writing this code with Optional like previous ones:
if (request.getLockVehicle() != null) {
if (request.getLockVehicle()) {
expressionMapping.add(campaign.lockVehicle.isNotNull());
} else {
expressionMapping.add(campaign.lockVehicle.isNull());
}
}
What about use enum to declare all fields from Request and use it as common part of the code. I did not check it, this is only to show my approach:
public enum RequestField {
PLANT_ID(Request::getPlantId, (val, campaign) -> campaign.plant.id::contains),
TITLE(Request::getTitle, (val, campaign) -> campaign.title::containsIgnoreCase),
CAMPAIGN_NUMBER(Request::getCampaignNumber, (val, campaign) -> campaign.campaignNumber::like),
// ... more fields here ...
;
private final Function<Request, Optional<Object>> get;
private final BiFunction<Object, Campaign, BooleanExpression> map;
RequestField(Function<Request, Object> get, BiFunction<Object, Campaign, BooleanExpression> map) {
this.get = get.andThen(Optional::ofNullable);
this.map = map;
}
public static List<BooleanExpression> getBooleanExpressions(Request request, Campaign campaign) {
if (request == null)
return Collections.emptyList();
List<BooleanExpression> res = new LinkedList<>();
for (RequestField field : values())
field.get.apply(request)
.map(r -> field.map.apply(r, campaign))
.ifPresent(res::add);
return res.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(res);
}
}
And your client code will be looking like:
List<BooleanExpression> booleanExpressions = RequestField.getBooleanExpressions(request, campaign);
P.S.
Your last code could be look like:
if (request.getLockVehicle() != null)
expressionMapping.add(request.getLockVehicle() ? campaign.lockVehicle.isNotNull() : campaign.lockVehicle.isNull());
The aim of using Optional is informing who is calling that method / parameter that it could be null.
In the first part of your code, you are not getting any advantage from this, you are just rewriting some code wrapping it around Optional logic but, as you said, without any "reusable" purpose.
A useful way is using it as returning value of a method: for example, if you know that your title could be null, you can refactor your getter like
public Optional<String> getTitle(){
return Optional.ofNullable(this.title); //I'm guessing the 'title' variable here
}
This will help you: every time you call getTitle() , you will know that could be null, because you are obtaining an Optional<String> instead of a String.
This will bring then you to:
request.getTitle().ifPresent(title-> title.doSomething())
// you can also add something like .orElse("anotherStringValue")
The second example could be reworked as the first one, making the return of getLockVehicle() as Optional<Boolean>, even if I suggest here setting that with a default value in your class, probably to false... Optional<Boolean> is pretty senseless imho
Hope this helps clearing your mind

How does one go about chaining several ChannelFuture objects with Netty?

I have a hashmap that contains ip/port information and a message that has to be sent to the whole list.
So I decided to create a small method that accepts the hashmap and the message and does this. It looks something like this:
public static ChannelFuture sendMessageTo(Map<JsonElement, JsonObject> list, String message) {
Set<JsonElement> keys = list.keySet();
for (JsonElement key : keys) { //iterate through the map
ChannelInboundHandler[] handlers = {
new MessageCompletenessHandler(),
new MessageRequest(message),
};
JsonObject identity = list.get(key);
ChannelFuture f = connectWithHandler(identity.get("ip").getAsString(), identity.get("port").getAsInt(), handlers); //to the following ip/port send message and return ChannelFuture
}
return result; //here result should be a ChannelFuture that when .addListener is added it should be called only when ALL the ChannelFuture-s from the for loop have finished(a.k.a. all messages have been sent)
}
The comments should explain the situation clearly enough.
The question is how do I implement this ChannelFuture result.
I know I can .sync() the ChannelFuture-s, but this defeats the purpose of async networking.
P.S.: I essentially want to have the functionality described here https://twistedmatrix.com/documents/16.2.0/api/twisted.internet.defer.DeferredList.html but am failing to find an equivalent.
In general what you're trying to achieve is not really correct async way. However netty has an utility class for that kind of task - DefaultChannelGroupFuture.java. However it is package private and used only for DefaultChannelGroup.java which indeed does what you described in your code. So you could easily copy this DefaultChannelGroupFuture and use it. To be more specific :
Collection<ChannelFuture> futures = ArrayList<ChannelFuture>();
...
//here you add your futures
ChannelFuture f = connectWithHandler(identity.get("ip").getAsString(), identity.get("port").getAsInt(), handlers);
futures.add(f);
...
DefaultChannelGroupFuture groupOfFutures = new DefaultChannelGroupFuture(futures, executor);
if (groupOfFutures.sync().isSuccess()) {
}
Have in mind you'll need to change DefaultChannelGroupFuture for your needs.

jOOQ - multiple bindings

I have a map of binding values:
final Map<String, Object> values = ...;
Before executing the query, I loop through the binding parameters and bind its value as following:
final ResultQuery<Record> q = ...;
for (final Param p : q.getParams().values()) {
if (p.getParamName() != null) {
q.bind(p.getParamName(), values.get(p.getParamName()));
}
}
When the same binding is used multiple times, this seems to fail:
final ResultQuery<Record> q = create.select().from(DSL.table("my_table"))
.where((DSL.field("identifier").eq(DSL.param("binding"))
.and(DSL.field("identifier").eq(DSL.param("binding")))));
... code above ...
create.fetch(q);
In the generated query, only one of the bindings is filled in. The other is null.
Is there an alternative approach? The same binding parameter can only be used once, or is this a bug?
(I know this query doesn't make much sense, it is only for demonstrating the problem.)
The issue was probably solved if we could do the following instead, but it is not possible since getParams() returns Param<?> and not Param<Object>:
for (final Param p : q.getParams().values()) {
if (p.getParamName() != null) {
p.bind(values.get(p.getParamName()));
}
}
update - The statement above is incorrect, since getParams() returns a Map<String, Param> and not Map<String, Collection<Param>>. It still woud be useful to bind a Param directly though.
This is a bug in jOOQ 3.5.2 (#4056).
Another workaround apart from the one you've found would be to make sure that both instances of "binding" are in fact the same, you should probably externalise the bind value:
Param<Object> binding = DSL.param("binding");
final ResultQuery<Record> q = create.select().from(DSL.table("my_table"))
.where((DSL.field("identifier").eq(binding)
.and(DSL.field("identifier").eq(binding))));
Now, you'll explicitly create a single bind value that is used two times in the AST.

MultivaluedMap in RESTEasy and square brackets

I'm building something of a relay server, using JAX-RS. I need to be able to extract any query parameters from a GET request and then re-wrap them into another request, to pass along to another server. I'm unfamiliar with MultivaluedMap, but I just figured out what is happening. The UriInfo class's getQueryParameters method returns a MultivaluedMap<String, String>. What bit me, unexpectedly, is that the values of each parameter are List values, even though they purport to be String values (by the way I read the JavaDoc).
In other words, if I have a key-value pair of foo=bar, in the URL query string, when I extract the parameter, it comes out as foo=[bar]. This totally throws me for a loop (a 500 Server Error, actually) when I try to re-wrap the parameter and send it along to the other server.
Is there another way to handle unpacking the query string from a Request, and then re-packing it for another Request? I'm including some code to illustrate my issue:
#GET
#Path("parameters")
public Response showParameters(#Context UriInfo uriInfo) {
MultivaluedMap<String, String> parameters = uriInfo.getQueryParameters();
StringBuffer sb = new StringBuffer();
sb.append("<h4>Parameters:</h4>");
if (parameters != null) {
sb.append("<ul>");
Iterator it = parameters.keySet().iterator();
while (it.hasNext()) {
String key = (String)it.next();
sb.append("<li>")
.append(key)
.append(": ")
.append(parameters.get(key))
.append("</li>");
}
sb.append("</ul>");
}
else {
sb.append("<p>None</p>");
}
return Response.ok(sb.toString()).build();
}
So, in summary, what gets printed out from the code above, if the request has query parameters, is something like this:
Parameters:
key1: [value1]
key2: [value2]
key3: [value3]
Is there another way to unpack/re-pack, and maybe avoid this whole issue altogether? Thanks.
Complete answer
#Jack deserves credit for pointing me in the right direction, and I am marking his answer as correct, but here is what I got working.
Client client = ClientBuilder.newClient();
// Assume instance variable providing URI (without query string).
WebTarget target = client.target(theRequestUri);
// Instance variable uriInfo.
MultivaluedMap<String, String> parameters = uriInfo.getQueryParameters();
if (parameters != null && parameters.size() > 0) {
Iterator it = parameters.keySet().iterator();
String key = null;
StringTokenizer st = null;
while (it.hasNext()) {
key = (String)it.next();
// RESTEasy API is quirky, here. It wraps the values in square
// brackets; moreover, each value is separated by a comma and
// padded by a space as well. ([one, two, three, etc])
st = new StringTokenizer(parameters.get(key).toString(), "[,]");
while (st.hasMoreTokens()) {
target = target.queryParam(key, st.nextToken().trim());
}
}
}
// Instance method: getContentType.
Response response = target.request().accept(getContentType()).get();
Its because of MultivaluedMap interface.
public interface MultivaluedMap<K, V> extends Map<K, List<V>> {
It returns the values as List.
Instead of parameters.get(key) try parameters.getFirst(key)
Note that this will drop the other values for the same parameter. It is possible to send multiple values for same parameter while making a rest call such as blahblah:8080?foo=bar1&foo=bar2. With getFirst() call you will get bar1 value only. If you are sure you will not get multiple calls, you can go with getFirst(key) approach
--- UPDATED ---
Based on your comments, it seems you need to iterate over the multivalued map and call queryParam on the WebTarget instance. I understand you are looking for a library/straight forward way to do this. I did not try RESTEasy. But code I believe should be simple enough.
multiValuesMap?.each { key, values->
webTargetInstance = webTargetInstance.queryParam(key, values as Object[])
}

Play2 calling multiple webservices with AsyncResult (Java)

I have a Play 2.1 controller in Java, and I need to call an external webservice to get some data. Then with this data result, I must call another web service with n calls, corresponding to the n results from the first webservice call.
For performance issues, I want to make the n calls in separated threads using promises.
So I would have a loop like this:
List<String> firstResults = WS.url("http://...") ///...blablabla
for(String keyword : firstResults){
Promise<ResultType> promise = play.libs.Akka.future(
new Callable<ResultType>() {
public Integer call() {
return //...
}
}
);}
How can I synchronize the n promises and then reduce the results in one response (a list of all results) using the Async API, and then return the http responses only when all calls are finished?
Not being able to know the number of calls make the problem more difficult ... (I can't declare promises as promise1, promise2 etc.)
Promise.waitAll is what you want:
List<String> firstResults = WS.url("http://...") ///...blablabla
List<Promise<? extends ResultType>> webServiceCalls = new ArrayList<>;
for(String keyword : firstResults){
Promise<ResultType> promise = WS.url("http://...?keyboard=" + keyword).get().map(
// function of Response to ResultType
);
webServiceCalls.add(promise);
}
// Don't be confused by the name here, it's not actually waiting
Promise<List<ResultType>> results = Promise.waitAll(webServiceCalls);
return async(results.map(new Function<List<ResultType, Result>>() {
public Result apply(List<ResultType> results) {
// Convert results to ResultType
}
});

Categories