Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to make use of Akka future and play promises in my Play2 application

When reading up on the play2 documentation I found this:

Because of the way Play 2.0 works, action code must be as fast as possible (i.e. non blocking). So what should we return as result if we are not yet able to compute it? The response should be a promise of a result!

Wow! This of course made me interested in playakka and akka. I'm currently building an autocomplete application that is integrating with elasticsearch, so this would be a perfect fit!

Controller:

public class AutoComplete extends Controller {

    @BodyParser.Of(value = BodyParser.Json.class)
    public static Result complete(final String term) {

        F.Promise<List<String>> list = Akka.future(new Callable<List<String>>() {
            public List<String> call() throws Exception {
                List<String> list = IndexService.find(term);
                return list;
            }
        });    

        return async(list.map(new F.Function<List<String>, Result>() {
            @Override
            public Result apply(List<String> list) throws Throwable {
                return ok(Json.toJson(list));
            }
        }));
}

Service:

public static List<String> find(final String term) {

        IndexQuery < SearchWord > query = SearchWord.find.query();
        query.setQuery("{\n" +
                "    \"bool\": {\n" +
                "        \"should\": [\n" +
                "            {\n" +
                "                \"text\": {\n" +
                "                    \"search_word.ngrams\": {\n" +
                "                        \"operator\": \"and\",\n" +
                "                        \"query\": \""+term+"\"\n" +
                "                    }\n" +
                "                }\n" +
                "            },\n" +
                "            {\n" +
                "                \"text\": {\n" +
                "                    \"search_word.full\": {\n" +
                "                        \"boost\": 1,\n" +
                "                        \"query\": \""+term+"\"\n" +
                "                    }\n" +
                "                }\n" +
                "            }\n" +
                "        ]\n" +
                "    }\n" +
                "}");
        IndexResults<SearchWord> indexResults = SearchWord.find.search(query);

        List<String> list = new ArrayList<String>();
        for(SearchWord word : indexResults.getResults()){
            list.add(word.getWord());
        }

        return list;
    }
}

SearchWord:

@IndexType(name = "search_word")
public class SearchWord extends Index {

    // Find method static for request
    public static Index.Finder<SearchWord> find = new Index.Finder<SearchWord>(SearchWord.class);

    public enum WordType {
        NAME,
        STRONG_SEARCH_WORD,
        WEAK_SEARCH_WORD,
        BANNED
    }

    private String word;
    private WordType wordType;

    public SearchWord() {
    }

    public SearchWord(IndexWord indexWord) {
        super.id = ""+indexWord.getId();
        this.word = StringUtils.lowerCase(indexWord.getWord());
        this.wordType = WordType.valueOf(indexWord.getType());
    }

    public String getId() {
        return super.id;
    }

    public void setId(String id) {
        super.id = id;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public WordType getWordType() {
        return wordType;
    }

    public void setWordType(WordType wordType) {
        this.wordType = wordType;
    }

    @Override
    public Map toIndex() {
        HashMap map = new HashMap();
        map.put("id", super.id);
        map.put("word", word);
        map.put("word_type", wordType.toString());
        return map;
    }

    @Override
    public Indexable fromIndex(Map map) {
        if (map == null) {
            return this;
        }
        this.word = (String) map.get("word");
        this.wordType = WordType.valueOf((String)map.get("word_type"));
        return this;
    }


}

The code works very well but I must say that I'm not that sure that I have implemented this correctly. I'm really struggling to understand the documentation. So my questions are basically:

  1. Have I implemented the Future and Promise correctly?
  2. Would it be better to create a custom actor, and in that actor perform the index search, like the example in the docs:

=====

 return async(
        Akka.asPromise(ask(myActor,"hello", 1000)).map(
          new Function<Object,Result>() {
            public Result apply(Object response) {
              return ok(response.toString());
            }
          }
        )
      );
  1. Maybe you have some great example that I have not found yet?
like image 505
jakob Avatar asked Jan 03 '13 10:01

jakob


1 Answers

AFAIK, your code is totally ok.

I may be wrong, but I think that the second option is strictly equivalent to the first one, since the Akka.future() method is a wrapper around the Akka.promise() method.

From the Akka class source code of Play 2.0.4:

/**
 * Executes a block of code asynchronously in the application Akka Actor system.
 */
public static <T> Promise<T> future(java.util.concurrent.Callable<T> callable) {
    return asPromise(akka.dispatch.Futures.future(callable, system().dispatcher()));
}
like image 80
ndeverge Avatar answered Nov 14 '22 22:11

ndeverge