I am trying to learn the Play Framework 2.4. I am trying to get the time it takes to access different webpages asynchronously using Promise. Below is the code for that:
final long start = System.currentTimeMillis();
F.Function<WSResponse,Long> timing = new F.Function<WSResponse, Long>() {
#Override
public Long apply(WSResponse wsResponse) throws Throwable {
return System.currentTimeMillis()-start;
}
};
F.Promise<Long> google = ws.url("http://www.google.com").get().map(timing);
F.Promise<Long> yahoo = ws.url("http://www.yahoo.com").get().map(timing);
F.Promise<Long> bing = ws.url("http://www.bing.com").get().map(timing);
As you can see I am using the get function to get the requested pages and putting the result in a Future Promise. Then I convert/map it to long. What I am not able to do is how do I compose these three promises into one and once all of the three promises are redeemed convert/map it to json and return the result. In earlier versions of Play it could have been done by F.Promise.waitAll(google,yahoo,bing).map(...) however I am unable to do it in Play 2.4. Please advice
EDIT1: Based on the answer below i used sequence like below:
return F.Promise.sequence(google, yahoo, bing).map(new F.Function<List<Long>, Result>() {
#Override
public Result apply(List<Long> longs) throws Throwable {
Map<String, Long> data = new HashMap<String, Long>();
data.put("google", google.get());
data.put("yahoo", yahoo.get());
data.put("bing", bing.get());
return ok(Json.toJson(data));
}
});
However, i am getting error that google.get() method cannot be resolved and that Json cannot be applied. What am i missing here?
EDIT 2. The Json error was fixed by using return ok((JsonNode) Json.toJson((Writes<Object>) data)); However, i am still not able to resolve the earlier error that google.get() method cannot be resolved in the line data.put("google", google.get());
EDIT 3. It seems Play2.4 has no get() method which returns the value of a Promise once it has been redeemed. What should i use then?
waitAll has been replaced with F.Promise.sequence.
From the docs
public static <A> F.Promise<java.util.List<A>> sequence(java.lang.Iterable<F.Promise<A>> promises)
Combine the given promises into a single promise for the list of results. The sequencing operations are performed in the default ExecutionContext.
Parameters:
promises - The promises to combine
Returns:
A single promise whose methods act on the list of redeemed promises
Update
Regarding the second half of the question, you don't need to call .get() because the promises have already completed.
In fact, you can get rid of the individual promise variables and just pass them directly into the sequence. The resulting list will contain results in the same order (Google first, then Yahoo, then Bing, in this case).
The whole thing should look something like this:
package controllers;
import java.util.HashMap;
import java.util.Map;
import play.libs.F;
import play.libs.Json;
import play.libs.ws.WS;
import play.libs.ws.WSResponse;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Results;
public class Application extends Controller {
public F.Promise<Result> index() {
final long start = System.currentTimeMillis();
final F.Function<WSResponse,Long> timing = response -> System.currentTimeMillis() - start;
return F.Promise.sequence(WS.url("http://www.google.com").get().map(timing),
WS.url("http://www.yahoo.com").get().map(timing),
WS.url("http://www.bing.com").get().map(timing))
.map(list -> {
Map<String, Long> data = new HashMap<>();
data.put("google", list.get(0));
data.put("yahoo", list.get(1));
data.put("bing", list.get(2));
return data;
})
.map(Json::toJson)
.map(Results::ok);
}
}
Finally, since Play 2.4 requires Java 8, this is a good opportunity to play around with lambdas!
Related
Given I have a class that uses some kind of searcher to get and display a list of URLs, like this:
package com.acme.displayer;
import com.acme.searcher.SearcherInterface;
class AcmeDisplayer {
private SearcherInterface searcher;
public AcmeDisplayer(SearcherInterface searcher) {
this.searcher = searcher;
}
public void display() {
List<String> urls = searcher.getUrls();
for (String url : urls) {
System.out.println(url);
}
}
}
Whereas the SearcherInterface looks like the following:
package com.acme.searcher;
public interface SearcherInterface {
List<String> getUrls();
}
There's multiple implementations of these searchers. (One, for instance, only returns a hardcoded list of Strings for testing purposes).
Another one, however, performs HTTP Requests to whatever API and parses the response for URLs, like so:
package com.acme.searcher.http;
import com.acme.searcher.SearcherInterface;
public class HttpSearcher implements SearcherInterface {
private RequestPerformerInterface requestPerformer;
private ParserInterface parser;
public HttpSearcher(RequestPerformerInterface requestPerformer, ParserInterface parser) {
this.requestPerformer = requestPerformer;
this.parser = parser;
}
List<String> getUrls() {
InputStream stream = requestPerformer.performRequest();
return parser.parse(stream);
}
}
The splitting of such an HTTP request is done because of seperation of concerns.
However, this is leading to a problem: A Parser might only be built for a certain API, which is represented by a certain RequestPerformer. So they need to be compatible. I've fiddled around with generic types for such a structure now, i.e. having a TypeInterface that both arguments of HttpSearchers constructor should implement, but I didn't get it working... Another approach would be to just implement a check in one class if the other one is compatible with it, but that seems ugly.
Is there any way to achieve such a grouping of RequestPerformers and Parsers by the API they're handling? Or is there something wrong with the architecture itself?
Your HttpSearcher seems like such a device to group these 2 together. You could create a factory class that returns HttpSearcher and other classes like it, and code that factory to group the compatible RequestPerformers and Parsers together.
The reason why I wouldn't advice leveraging the type system, e.g. through generics, is that the type InputStream can guarantee nothing about the format/type of data it holds. Separating the responsibility of getting the raw data, and parsing seems like a good idea, but you will still have to 'manually' group the compatible types together, because only you know what format/type of data the InputStream will hold.
I have created AWS Java Lambda project from ECLIPSE IDE. In the handle request section I want to process the request and insert it into a AWS DynamoDB table. In
I can see this can be easily done using node.js. Lot of code samples are available. Is there a proper JAVA help available. I am new to JAVA and struggling to find this. Any help is appreciated.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class LambdaHandler implements RequestHandler<Object, Object> {
#Override
public Object handleRequest(Object input, Context context) {
context.getLogger().log("Input: " + input);
// TODO: implement DYNAMO DB INSERT
return input;
}
}
Use the api in package com.amazonaws.services.dynamodbv2 (maven dependency artifactID= aws-java-sdk-dynamodb )
AWSCredentials creds = new BasicAWSCredentials("myacceskey","mysecretkey");
AmazonDynamoDBClient dyndbclient = new AmazonDynamoDBClient(creds);
String tableName = "myDynamoDbTable"
Map<String, AttributeValue> dbItem = new HashMap<String, AttributeValue>();
//TODO put key/values from request in dbItem
dyndbclient.putItem(tableName, dbItem);
You may also want to redefine your lambda handler function, so that you have a better cast input parameter than your current (Object input )
I wrote an event handler for Stash to send out messages via a messaging bus architecture. Here's an example of one from my fedmsgEventListener class:
#EventListener
public void opened(PullRequestOpenedEvent event)
{
HashMap<String, Object> message = prExtracter(event);
String originProjectKey = ((HashMap<String, Object>)message.get("source")).get("project_key").toString();
String originRepo = ((HashMap<String, Object>)message.get("source")).get("repository").toString();
String topic = originProjectKey + "." + originRepo + ".pullrequest.opened";
sendMessage(topic, message);
}
It gets an event, extracts information out of it, constructs a topic based on the information in the event, and invokes a method to send the message. I need to write unit tests for all of these event handlers.
Here is the class that runs the first test I am attempting to implement:
import org.junit.Test;
import com.cray.stash.MyPluginComponent;
import com.cray.stash.MyPluginComponentImpl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MyComponentUnitTest
{
#Test
public void testMyName()
{
MyPluginComponent component = new MyPluginComponentImpl(null);
assertTrue(component.openPullRequest().contains(".pullrequest.opened"));
}
}
and then here is the class and method that the test calls:
import com.atlassian.sal.api.ApplicationProperties;
import com.atlassian.stash.event.pull.*;
import org.mockito.Mock;
import static org.mockito.Mockito.*;
public class MyPluginComponentImpl implements MyPluginComponent
{
#Mock private PullRequestEvent event;
#Mock private PullRequestOpenedEvent opened;
#Mock private FedmsgEventListener fedmsgEventListener;
public MyPluginComponentImpl(ApplicationProperties applicationProperties)
{
this.applicationProperties = applicationProperties;
}
public String openPullRequest()
{
fedmsgEventListener.opened(opened);
return fedmsgEventListener.getTopic();
}
}
As of now, the method throws a NullPointerException because the fedmsgEventListener and the PullRequestEvent are both mocked objects and therefore null.
Is this the best way to go about unit testing this scenario? From a high level, this is all I want to do: trigger the event, see that the topic got changed to a string including a certain string.
You are using Mockito completely wrong. Sorry. First of all, #Mock doesn't work without using initMocks or MockitoJUnitRunner, but I wouldn't do it that way anyway. A mock is not null; you should be able to call methods on mocks; in your case you didn't initialize / create the mocks and that's why they were null.
First, identify the class you're trying to test. It looks like it's FedmsgEventListener here. Then, interact with a real instance of that class using mock objects and data structures instead of real objects that have dependencies and so forth. Note, I am using Hamcrest 1.3 here.
A mocking based test is built up in three phases:
Create - Create your mocks, and then state that "when" an interaction with that mock occurs, do something.
Interact - Interact with your objects in the way you're trying to test.
Verify - Use Mockito.verify and JUnit/Hamcrest assert methods to ensure that things worked the way you expected.
You might do something like this:
import static org.mockito.Mockito.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
private HashMap<String, Object> createMessageDetails(String project_key, String repository) {
HashMap<String, Object> details = new HashMap<>();
details.put("project_key", project_key);
details.put("repository", repository);
return details;
}
public class FedmsgEventListenerTest {
#Test
public void testOpened() {
// when
PullRequestOpenedEvent event = mock(PullRequestOpenedEvent.class);
when(event.someMethodForPrExtracterYouHaventShownMe()).thenReturn(createMessageDetails("myKey", "myRepo"));
// then
FedmsgEventListener listener = new FedmsgEventListener();
listener.opened(event);
// verify
assertThat(event.getTopic(), containsString(".pullrequest.opened"));
verify(event).someMethodForPrExtracterYouHaventShownMe();
}
}
This code is probably not exactly what you need, but you haven't shown me enough of the code you're trying to test for me to get it exactly right. However, I think this should be enough to get you started.
As an aside, if you aren't able to create a real instance of your class with mocked dependencies, then that is a code smell and your code should be refactored. This is one reason why statics are such a bad idea, because if your code is accessing global state via statics then you have to set up the global state with your statics. Make your class able to work with mock dependencies, pass them as arguments to the constructor, specify the mock behavior with when, and then assert / verify the results.
I have provided a callback to a third party library that calls the provided method at various times providing me with an object that has changed. I am then carrying out an async web request to get further details and set them on that object, below is a made up similar example;
public void update(Person person) {
if (person.getId() == -1) {
mService.getPersonDetails()
.flatMap(..)
.skip(..)
.subscribe(personResult -> person.setId(personResult.getId()))
}
}
The update is called quite a few times and should only executes the query if the object has no ID. The problem is that at least two requests get sent off as the first query has not yet completed.
How can I synchronise this method call so that only one request is sent for each Object that get passed via the callback? I only want to block requests for that exact Object, so if the update() is supplying different objects it would be ok for new requests to be sent out.
The solution provided by Adam S looks good but soon or later will cause OOM problems. It is due to distinct operator which has to store all unique values.
Other option that comes to my mind is usage of ConcurrentMap to store processed persons and doOnTerminate to clean it.
private Map<Person, Boolean> map = new ConcurrentHashMap<>();
public void update(final Person person) {
if (person.getId() == -1) {
if(map.putIfAbsent(person, true)==null){
mService.getPersonDetails()
.flatMap(..)
.skip(..)
.doOnTerminate(()->map.remove(person))
.subscribe(personResult -> person.setId(personResult.getId()))
}
}
}
You can filter the inputs to your observable using the distinct operator. Here's a general idea of how you could do that using a PublishSubject (JavaDoc) (note this is written from memory, I haven't tested this):
private PublishSubject<Person> personSubject;
public void update(Person person) {
if (personSubject == null) {
personSubject = new PublishSubject();
personSubject
.filter(person -> person.getId() == -1)
.distinct()
.flatMap(person -> mService.getPersonDetails())
.skip(..)
.subscribe(personResult -> person.setId(personResult.getId()));
}
personSubject.onNext(person);
}
You will, of course, have to either implement the equals method on your Person class (which, as Marek points out, will result in all objects passed in being cached in memory) or implement the distinct(Func) variant.
That method takes a 'key selector' function used to differentiate between objects. If your objects are fairly heavy and you're concerned about memory (if you're on Android, for example) this might be a better path. Something like this:
.distinct(new Func1<Person, Integer>() {
#Override
public Integer call(Person person) {
return person.hashCode();
}
})
Well you can just simply synchronize it.
public synchronized void update(Person person)
Sorry, I'm new to Play Framework.
I use it with Java API.
Let's say I want to have a controller action that runs some kind of import and displays result after import is finished.
Import requires expensive HTTP communication with the 3rd party service (fetching data from 3 URLs, processing data, updating database after all 3 resources were processed).
So I'd like to implement an import itself as a Promise in controller (Controller shouldn't be aware of import implementation).
Then I'd like to run fetching the data from URLs and processing in 3 parallel threads. I think it would be nice to implement it as a 3 separate Promises.
Database should be updated only when (and if) all three promises were completed successfully.
And finally controller should be notified after database was updated.
I'm able to implement the whole import as a Promise, but I don't know how to implement nested promises.
Could you suggest how to implement it or correct me if I'm trying to use wrong approach?
You can achieve this with flatmap. The syntax in Java is sadly a bit clunky because of the anonymous interfaces (will get better with Java 8 and lambdas). Promise<T>.flatMap accepts a Function<T, Promise<U>> and will return a Promise<U>. This means you can nest flatMaps from all your three operations and collect them with a flatmap, like this:
final Promise<String> promise1 = Promise.pure("one");
final Promise<String> promise2 = Promise.pure("two");
final Promise<String> promise3 = Promise.pure("three");
Promise<String> allThreeCombined = promise1.flatMap(new Function<String, Promise<String>>() {
#Override
public Promise<String> apply(final String result1) throws Throwable {
return promise2.flatMap(new Function<String, Promise<String>>() {
#Override
public Promise<String> apply(final String result2) throws Throwable {
return promise3.map(new Function<String, String>() {
#Override
public String apply(String result3) throws Throwable {
return result1 + result2 + result3;
}
});
}
});
}
});
If there is no special meaning to each of the different things you are fetching - for example if they are to be treated as a list of values you can also use Promise.sequence() which accepts a list of Promise<T> and return a Promise<List<T>> so you can react on all values arriving.