How to avoid if - java

I have a Request object with field request_type and number of other fields. request_type can be 'additionRequest' , 'deletionRequest' 'informationRequest'.
Based on request_type other fields in Request object are processed differently.
My simple minded approach is
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
How I can avoid these if statements and still apply proper algorithm?

If you want to avoid conditional statements then you can leverage object oriented features such as:
Map<String, Function<Request, Result>> parsers = new HashMap<>();
parsers.put("additionRequest", request -> {
// parse and generate Result here
return result;
});
Result result = parsers.get(request.request_type).apply(request);

Seems to me that perhaps a Command Pattern could come in handy here. If you make an object structure of these commands and encapsulate the algorithm that you want to execute within the object, then you can construct the specific sub-objects and later on use "execute" method of the command to invoke the algorithm. Just make sure you are using polymorphic references:
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
will become
void theRequestExecutor(Reqest req) {
req.executeAlgorithm()
}
https://en.wikipedia.org/wiki/Command_pattern

Use HashMap<RequestType, RequestHandler> for this case. RequestHandler can be an interface which will be implemented for each situation you want to handle.
Hope this help.

You can create Map of key:String, value :Function of RequestType ReturnType. Depending on type of request it will call corresponding function.
Example:
Map<String, Function<RequestType, ResponseType> requestProcessors = new HashMap<>;
requestProcessors.add("additionRequest", this::methodToHandleAddRequest);
requestProcessors.add("deletionRequest", this::methodToHandleDeleteRequest);
Inside request handler do
return this.requestProcessors.get(request.request_type).apply(request);
Note you may have to create response interface if different responses are different. Different response type will inherit from response interface

The object-oriented solution is always to include the logic with the data. In this case include whatever you want a request to do in the request object itself, instead of (presumably) passing out pure data.
Something like
public interface Request {
Response apply();
}
Anything else, like creating a map of functions, creating a thin abstraction layer or applying some pattern is a workaround. These are suitable only if the first solution can not be applied. This might be the case if the Request objects are not under your control, for example generated automatically or third party classes.

Related

How to consume a Spring HAL/HATEOAS API in Java using purely Jackson, not Spring

We are trying to create a Java client for an API created with Spring Data.
Some endpoints return hal+json responses containing _embedded and _links attributes.
Our main problem at the moment is trying to wrap our heads around the following structure:
{
"_embedded": {
"plans": [
{
...
}
]
},
...
}
When you hit the plans endpoint you get a paginated response the content of which is within the _embedded object. So the logic is that you call plans and you get back a response containing an _embedded object that contains a plans attribute that holds an array of plan objects.
The content of the _embedded object can vary as well, and trying a solution using generics, like the example following, ended up returning us a List of LinkedHashMap Objects instead of the expected type.
class PaginatedResponse<T> {
#JsonProperty("_embedded")
Embedded<T> embedded;
....
}
class Embedded<T> {
#JsonAlias({"plans", "projects"})
List<T> content; // This instead of type T ends up deserialising as a List of LinkedHashMap objects
....
}
I am not sure if the above issue is relevant to this Jackson bug report dating from 2015.
The only solution we have so far is to either create a paginated response for each type of content, with explicitly defined types, or to include a List<type_here> for each type of object we expect to receive and make sure that we only read from the populated list and not the null ones.
So our main question to this quite spread out issue is, how one is supposed to navigate such an API without the use of Spring?
We do not consider using Spring in any form as an acceptable solution. At the same time, and I may be quite wrong here, but it looks like in the java world Spring is the only framework actively supporting/promoting HAL/HATEOAS?
I'm sorry if there are wrongly expressed concepts, assumptions and terminology in this question but we are trying to wrap our heads around the philosophy of such an implementation and how to deal with it from a Java point of view.
You can try consuming HATEOS API using super type tokens. A kind of generic way to handle all kind of hateos response.
For example
Below generic class to handle response
public class Resource<T> {
protected Resource() {
this.content = null;
}
public Resource(T content, Link... links) {
this(content, Arrays.asList(links));
}
}
Below code to read the response for various objects
ObjectMapper objectMapper = new ObjectMapper();
Resource<ObjectA> objectA = objectMapper.readValue(response, new TypeReference<Resource<ObjectA>>() {});
Resource<ObjectB> objectB = objectMapper.readValue(response, new TypeReference<Resource<ObjectB>>() {});
You can refer below
http://www.java-allandsundry.com/2012/12/json-deserialization-with-jackson-and.html
http://www.java-allandsundry.com/2014/01/consuming-spring-hateoas-rest-service.html

Routing by param data type in Play

In Play, what is the best way to handle routing of endpoints based on the data type provided?
GET /my/:id controllers.MyController.getById(id: Long)
GET /my/:name controllers.MyController.getByName(name: String)
getById should be called for GET /my/1.
getByName should be called for GET /my/bob (because bob does not cast to Long)
This may not always be the best endpoint design, but if it's required what is the best way to achieve this?
One workaround as noted in this answer is to create single controller methods which try the conversion for you.
GET /my/:arg controllers.MyController.getByAny(arg: String)
Result getByAny(String arg) {
try {
getById( Long.parseLong(arg)) ;
} catch (Exception e) {
return getByName(arg)
}
}
A downside to this is if using Swagger docs you no longer have separate documentation for each route.
You also must do this individually for each conflicting group of routes.
Is there a way to add similar functionality, without breaking documention, to all Endpoints rather than one by one?
Try using custom patterns.
GET /items/$id<[0-9]+> controllers.Items.getById(id: Long)
GET /items/$name<[a-zA-Z]+> controllers.Items.getByName(name)
In fact the second one could probably be simplified to:
GET /items/:name controllers.Items.getByName(name)
When multiple routes match the first one to match should be picked.

How to pass list of parameters in rest get call (like filter object in eCommerce )

In my application, there is a requirement of getting data based on some parameters.
I just want to what is the better way to do.
The one way is, I can pass the list of parameters as a path variable.
The second way is, I can pass the request body, I think it is vague and I am not sure it is possible or not.
You can find the code below:
#GetMapping("/cities/{cityName}/latitude/{latitude}/longitude/{longitude}/cityId/{cityId}/street/{street}")
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude() {
}
I just want to know how can I achieve the same.
There is one more question, E-commerce companies have big filter criteria so how they are achieving.
Although there is no hard & fast rule but I generally avoid sending a body in GET request because it's a bad design. You should also refer to this SO Post which contains discussion about using body in GET request. It's an opinionated post and there is no clear YES or NO, but you will get an idea.
HTTP GET with request body
You can either use Path params or query params depending on what those field represent.
Regarding the difference or which to use when I am quoting this answer, which mentions that although there is no hard rule but generally it's better to use params which can uniquely identify the resource as Path param (e.g. id, name etc) and if your param is supposed to do something like filtering/sorting e.g. records after Jan 1 2019 , then go for query param.
Also personally in one of my APIs (which performs filtering), I am using a generic query param, where I pass on JSON object in my query. So basically my API needs to search an object based on variable/multiple attributes. E.g. I have in my db , objects which have certain voltage, current, size etc. values. So, request might come with a combination of 1 or more. So to keep my API flexible, I have provided a query param which can accept JSON object.
So I make a request like this:
{{SERVER}}/api/search?query={voltage:12V,size:10}
And in my API, I can convert this json object to corresponding POJO:
#GET
#Path("/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response search(#QueryParam("query") String queryParam) throws Exception
{
Myobj obj = new Gson().fromJson(queryParam, Myobj.class);
// rest of code
By passing the parameters in the path, you are restricting yourself to extend your API. If you want to extend your API, for example, if you want to filter with criteria as Street1 (or) Street2 then your path wouldnot support it and it will force you to update your API. It is better to pass criteria objects in the body or url parameter. Amazon India is passing criteria like below. I have choosen mobiles with criteria as Manufacturer = Samsung or MI, Storage as 8gb or 4gb and they simply appended the criteria in the query parameters.
There is a third way, Request Params.
#GetMapping
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude(#RequestParam("cityName") String cityName, #RequestParam("latitude") String latitude, #RequestParam("longitude") String longitude){
// Your code
}
For more: 16.3.3.3 Binding request parameters to method parameters with #RequestParam
Parameters using this annotation are required by default, but you can specify that a parameter is optional by setting #RequestParam's required attribute to false (e.g., #RequestParam(value="id", required=false)).
https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#mvc-ann-requestparam

What is the best way to get one param from form (Play2 Framework)?

I am want to create simple form for searching records via one parameter (for example, name).
Seems like creating a class with one property (name) and than use helpers for forms - is not a best way.
Is there any examles how can I get POST data from request and fetch property value from that data?
Thanks a lot for wasting your time.
You already answered your own question, I just want to provide some more information:
You are right about creating a class with one single property, however keep in mind that you can use validation annotations (like #Required, #Email, etc.) in this class - so if there is some (super) complex logic behind this property this might also be a valuable option.
The second solution would be to use DynamicForms - you use them when you don't really have a model that is backing up the submission form. It goes like this:
public static Result index() {
DynamicForm requestData = Form.form().bindFromRequest();
String name = requestData.get("name");
return ok(name);
}
And of course the third option to get the values is like you mentioned:
String name = request().body().asFormUrlEncoded().get("name")[0];
If you do not what to use form validation, I don't think you need to create a class. Instead, you can use AJAX function like $.ajax(), that will be route to your specific controller function. Moreover, you can call your model function from your controller then at last return the result. The result will be caught by the $.ajax() function.
$.ajax
type: "POST"
url: url
data: data
success: success
dataType: dataType

Play Framework 2 - Call Web Service and return an object

I have a model which I want to populate with details from a web service. I'd like to do this asynchronously so as not to block a server thread. Let's pretend it's a login service.
Now what I want to do is fire a request to the remote server and ultimately return a User model object. So the method signature would look something like this:
public static User loginUser(String username, String password) {
I understand that to make an asynchronous call to a web service I should use a Promise:
Promise<WS.Response> wsPromise = WS.url("http://myserver.com/login")
.setContentType("application/json; charset=utf-8")
.post("... the username and password ...");
Which doesn't start the request yet. I could call get() on this object to make a blocking call to the service. This works.
To do this asynchronously, I thought I'd need to map this and somehow execute it.
Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
#Override
public User apply(WS.Response response) throws Throwable {
System.out.println(response.getBody());
return new User(... based on something extracted from the returned JSON ...);
}
});
Now how do I trigger this operation? If I call get() on the resultPromise, it makes the call but eventually fires a Timeout Exception. I can't use the async(...) method because that only returns me a Result.
Looking at other examples (https://github.com/jroper/play-promise-presentation/blob/master/src/main/java/controllers/Application.java), this seems to be the pattern. i.e. We always want to return a Result object. However, I can't query a Result object and I have no plan to send that specific object out to the user.
In fact, those examples seem to call a web service, map the JSON result to an object and then immediately map them back to the same JSON. Not much use when I want to pass the User (in my case) back to the calling function.
To be honest, I'm a little confused with the asynchronous nature of this anyway (you probably guessed that). In particular, this is really a blocking action as we have to wait for the web service to return a response. The documentation seems to indicate that using the Promise / Future patterns will avoid this blocking.
Bottom line is: How do I map the result of a web service call back to a model object without blocking a thread in the Play Framework server?
Feel free to abuse my lack of experience of the Play Framework...
This answer might come way too late, but will post it in case someone else nowadays still wonder the same and want an answer to the question.
There are two ways to achieve this, even though both make use of the same JsonNode class:
The first one is what you were expecting: "new User( ..something...)"
In this case you can make use of JsonNode's "get" method to obtain the specific data you need to create the User object (username and email in my example since you didn't specify the required fields).
Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
#Override
public User apply(WS.Response response) throws Throwable {
System.out.println(response.getBody());
JsonNode json = response.asJson();
return new User(json.get("username"), json.get("email"));
}
});
The other option is in case you know the Web Service does return a valid Json representation of an User object.
Personally I think this option is way easier and relys on fromJson's method from Json class.
Promise<User> resultPromise = wsPromise.map(new F.Function<WS.Response, User>() {
#Override
public User apply(WS.Response response) throws Throwable {
System.out.println(response.getBody());
return Json.fromJson(response.asJson(), User.class);
}
});
I hope this answer can help people wondering how to do this in a easy way.
.get() or .post(...) on a WS call actually does trigger the WS call to go out. (Don't confuse WS.get() with myPromise.get()... they are quite different).
The trick to keeping this sort of design fully async is to have Promises all the way down, from the Controller response, all the way down.
To do this, you must make judicious use of the map(), flatMat(), and sequence() methods in the Promise API.
In your particular case, you use the result of the WS call inside the code that does the map() that happens when the WS call returns. That is where you use the response to actually do things.
This paradigm is easier in Scala with "for comprehensions", but it's still possible in Java with chains of nested flatMap calls ended with a map call.

Categories