Junit test for unmarshalling method - java

I have to test my methods for unmarshalling functionality and I don't know how to do it. Can someone explain me?
Here are my methods:
#Service
public class MapperServiceImpl implements MapperService {
public void mappingContent(String directoryPath, String fileName) throws JAXBException {
File xmlFile = new File(directoryPath + "/" + fileName);
JAXBContext jaxbContext = JAXBContext.newInstance(Menu.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Menu menu = (Menu) jaxbUnmarshaller.unmarshal(xmlFile);
displayMappingContent(menu);
}
public void displayMappingContent(Menu menu) {
List<Content> contents = menu.getContents();
AtomicInteger count = new AtomicInteger(1);
System.out.println("Type : " + menu.getType().toUpperCase());
contents.stream()
.flatMap(content -> content.getFoods().stream()).toList()
.forEach(food -> System.out.println("NR. " + count.getAndIncrement()
+ "\nName : " + food.getName() + "\nPrice : " + food.getPrice()
+ "\nDescription : " + food.getDescription() + "\nCalories" + food.getCalories() + "\n"));
}
}

Your code is hard to test because your methods operate by side effects (that is, printing to a PrintStream) instead of returning values.
A simple approach to make your code more testable is to create two more methods in your class:
#Service
public class MapperServiceImpl implements MapperService {
public void mappingContent(String directoryPath, String fileName) throws JAXBException {
File xmlFile = new File(directoryPath + "/" + fileName);
displayMappingContent(unmarshallMenu(xmlFile));
}
public void displayMappingContent(Menu menu) {
System.out.println(renderMenu(menu));
}
public String renderMenu(Menu menu) {
AtomicInteger count = new AtomicInteger(1);
List<Content> contents = menu.getContents();
StringBuffer sb = new StringBuffer();
sb.append("Type : " + menu.getType().toUpperCase());
contents.stream()
.flatMap(content -> content.getFoods().stream()).toList()
.forEach(food -> sb.append("NR. " + count.getAndIncrement()
+ "\nName : " + food.getName() + "\nPrice : " + food.getPrice()
+ "\nDescription : " + food.getDescription() + "\nCalories" + food.getCalories() + "\n"));
return sb.toString()
}
public Menu unmarshallMenu(File xmlFile) {
JAXBContext jaxbContext = JAXBContext.newInstance(Menu.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Menu menu = (Menu) jaxbUnmarshaller.unmarshal(xmlFile);
}
}
Now the original two methods are trivial, and the new ones are simple to test.
You could also split your service into three: a #Component to unmarshall, a #Component to render menus, and the existing service which is a client of those components. Then when you test the service you can mock those components.

Related

How to make RestAssured.filters() Thread Safe?

I am using RestAssured for my API Automation and I am using ExtentReports to log the request and response. It works fine when I run in a single thread. But when I run in multiple threads, it doesn't seem to be thread-safe. Logs of one thread are getting clogged in the other. Below is my TestList Class:
TestListClass.Java
public class TestListener implements ITestListener, ISuiteListener {
String line = new String(new char[150]).replace('\0', '-');
ExtentReportHelper report;
ExtentReports extent;
ExtentTest test;
private StringWriter requestWriter;
private StringWriter responseWriter;
private PrintStream printRequestStream;
private PrintStream printResponseStream;
#Override
public void onTestStart(ITestResult result) {
log.info("\n");
log.info(line);
log.info(StringUtils
.center("Test case :--> \"" + result.getName() + "\" <--: about to start...", 130));
log.info(line + "\n\n");
test = extent.createTest(result.getName());
apiLogsReport();
}
private void apiLogsReport() {
requestWriter = new StringWriter();
responseWriter = new StringWriter();
printRequestStream = new PrintStream(new WriterOutputStream(requestWriter,"UTF-8"));
printResponseStream = new PrintStream(new WriterOutputStream(responseWriter,"UTF-8"));
RequestLoggingFilter requestLoggingFilter = new RequestLoggingFilter(printRequestStream);
ResponseLoggingFilter responseLoggingFilter = new ResponseLoggingFilter(printResponseStream);
RestAssured.filters(requestLoggingFilter, responseLoggingFilter);
}
#Override
public void onTestSuccess(ITestResult itr) {
log.info(line);
log.info(StringUtils.center(String.format("|%30s|", itr.getTestClass())
+ String.format("|%20s|", itr.getName()) + String.format("|%10S|", "passed"), 120));
log.info(line + "\n");
test.log(Status.PASS,
MarkupHelper.createLabel(itr.getName() + " PASSED ", ExtentColor.GREEN));
printRequestStream.flush();
printResponseStream.flush();
report.logRequestResponse(test, requestWriter, responseWriter);
}
#Override
public void onTestFailure(ITestResult itr) {
log.info(line);
log.info(StringUtils.center(String.format("|%30s|", itr.getTestClass())
+ String.format("|%20s|", itr.getName()) + String.format("|%10S|", "failed"), 120));
log.info(line + "\n");
test.log(Status.FAIL, MarkupHelper.createLabel(itr.getName(), ExtentColor.RED));
test.fail(itr.getThrowable());
printRequestStream.flush();
printResponseStream.flush();
report.logRequestResponse(test, requestWriter, responseWriter);
}
#Override
public void onTestSkipped(ITestResult itr) {
log.info(line);
log.info(StringUtils.center(String.format("|%30s|", itr.getTestClass())
+ String.format("|%20s|", itr.getName()) + String.format("|%10S|", "skipped"), 120));
log.info(line + "\n");
test.log(Status.SKIP,
MarkupHelper.createLabel(itr.getName() + " SKIPPED ", ExtentColor.ORANGE));
test.skip(itr.getThrowable());
printRequestStream.flush();
printResponseStream.flush();
report.logRequestResponse(test, requestWriter, responseWriter);
}
#Override
public void onStart(ITestContext context) {
report = new ExtentReportHelper();
extent = report.initReport(testNgHelper);
}
logRequestResponse method()
public void logRequestResponse(ExtentTest test, StringWriter requestWriter, StringWriter responseWriter) {
String[] requests = requestWriter.toString().split("Request method:");
String[] responses = responseWriter.toString().split("HTTP/1.1");
for (int i = 1; i < requests.length; i++) {
test.log(Status.INFO, "<pre>Request: " + requests[i] + "\nResponse: " + responses[i] + "</pre>");
}
}
Am I doing something wrong? Or doesn't RestAssured filters support multi-threaded execution? Please help me out if there is any other way to do so if it is not thread-safe.

Convert JSON to POJO having Json property as jsonObject having diffrent property

I have new requirement, I am creating REST API which has dynamic request (actions) and I want to convert that JSON request to POJO, I know how to convert JSON to POJO where key's are same, but not sure what to do when there are different content on objects.
My Json is as follow.
{
"name":"Workflow",
"actions": [
{
"name": "EDIT_PROPERTY",
"payload": {
"name": "city",
"value": "Pune"
}
},
{
"name":"SEND_EMAIL",
"payload":{
"from":"no-reply#yaho.com",
"to":"alpesh#yahoo.com",
"subject":"Try email",
"body":"content"
}
},
{
"name":"CREATE_TASK",
"payload":{
"user":1,
"type":"call",
"status":"open",
"note":"This is note content"
}
}
]
}
As you can see actions are set of Objects which has name and payload, now payload has different fields, I have predefined names. and each payload under action has predefined keys as you see.
I want to convert this to POJO something like
class Workflow{
String name;
Set<Action> actions;
}
class Action {
String name;
//What to add as payload
}
Thanks
Alpesh
This is what you can do :
JSON to POJO model :
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Event {
public String name;
public List<Action> actions;
#Data
public static class Action {
public String name;
Map<String, Object> payload;
}
}
public class TestJson {
private static String json = "{\n" +
" \"name\":\"Workflow\",\n" +
" \"actions\": [\n" +
" {\n" +
" \"name\": \"EDIT_PROPERTY\",\n" +
" \"payload\": {\n" +
" \"name\": \"city\",\n" +
" \"value\": \"Pune\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"name\":\"SEND_EMAIL\",\n" +
" \"payload\":{\n" +
" \"from\":\"no-reply#yaho.com\",\n" +
" \"to\":\"alpesh#yahoo.com\",\n" +
" \"subject\":\"Try email\",\n" +
" \"body\":\"content\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"name\":\"CREATE_TASK\",\n" +
" \"payload\":{\n" +
" \"user\":1,\n" +
" \"type\":\"call\",\n" +
" \"status\":\"open\",\n" +
" \"note\":\"This is note content\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}";
#SneakyThrows
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Event event = objectMapper.readValue(json, Event.class);
System.out.println(event);
}
}
When debugging, you'll notice that our objects have been filled accordingly:
Well, generally, I prefer any solution that does not involve Component Annotations. This is my own personal preference because eliminating constructors and method parameters is usually a giant headache. This is based on 1999 - 2006 Java Programming experience. If you have a need to dynamically generate classes and constructors or getters, then you may easily ignore or delete this answer. For me, JSON Parsing is practice right now.
In this posted answer, I have used the older Java JSON Library whose JavaDoc may be viewed here: javax.json.*. Here is my solution. It requires / expects that you write:
Your own toString() methods for your JSON Data Classes
Your own retrieve operations
The following code has output, and that is included at the end of this post. Usually I include a lot of Code Documentation. However, when the code is strictly parsing data, the code itself is usually so legible that more comments would clutter the retrieve and the toString() operations, so I have left them as is.
import java.util.*;
import javax.json.*;
import java.io.*;
public class Messages
{
public abstract static class Action
{
public final String name;
public Action(String name) { this.name=name; }
}
public static class EditProperty extends Action
{
public final String propName, propValue;
public EditProperty(String name, String value)
{ super("EDIT_PROPERTY"); this.propName=name; this.propValue=value; }
public String toString()
{
return
"Action: " + name + '\n' +
"Name: " + propName + '\n' +
"Value: " + propValue + '\n';
}
}
public static class SendEmail extends Action
{
public final String from, to, subject, body;
public SendEmail(String from, String to, String subject, String body)
{ super("SEND_EMAIL"); this.from=from; this.to=to; this.subject=subject; this.body=body; }
public String toString()
{
return
"Action: " + name + '\n' +
"From: " + from + '\n' +
"To: " + to + '\n' +
"Subject: " + subject + '\n' +
"Body: " + body + '\n';
}
}
public static class CreateTask extends Action
{
public final int user;
public final String type, status, note;
public CreateTask(int user, String type, String status, String note)
{ super("CREATE_TASK"); this.user=user; this.type=type; this.status=status; this.note=note; }
public String toString()
{
return
"Action: " + name + '\n' +
"User: " + user + '\n' +
"Type: " + type + '\n' +
"Status: " + status + '\n' +
"Note: " + note + '\n';
}
}
public static void main(String[] argv) throws IOException
{
Vector<Action> actions = new Vector<>();
Reader r = new FileReader("in.json");
JsonArray actionList = Json
.createReader(r)
.readObject()
.getJsonArray("actions");
for (JsonObject actionObj : actionList.getValuesAs(JsonObject.class))
{
JsonObject payload = actionObj.getJsonObject("payload");
Action action = null;
switch (actionObj.getString("name"))
{
case "EDIT_PROPERTY" : action = new EditProperty(
payload.getString("name"),
payload.getString("value")
); break;
case "SEND_EMAIL" : action = new SendEmail(
payload.getString("from"),
payload.getString("to"),
payload.getString("subject"),
payload.getString("body")
); break;
case "CREATE_TASK" : action = new CreateTask(
payload.getInt("user"),
payload.getString("type"),
payload.getString("status"),
payload.getString("note")
); break;
}
actions.add(action);
}
for (Action action : actions) System.out.println(action);
}
}
The class and inner-classes above would produce this output when invoked at the command line:
#cloudshell:~$ java Messages
Action: EDIT_PROPERTY
Name: city
Value: Pune
Action: SEND_EMAIL
From: no-reply#yaho.com
To: alpesh#yahoo.com
Subject: Try email
Body: content
Action: CREATE_TASK
User: 1
Type: call
Status: open
Note: This is note content
The JSON you show is actually a list of one object type;
specifically, the payload is just a Map of String to Object.
Once you parse the JSON,
your code will need to process each "different" payload type
based on the payload type.
Here is some sample code:
public class BlamMessage
{
private String name;
private Map<String, Object> payload;
...
}
public class MessageHolder
{
#JsonProperty("actions")
private List<BlamMessage> messageList;
private String name;
...
}

Jackson error: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token

Hey I have also problem here is my Json
[
{
"aimid": "12345"
},
{
"aimid": "333674"
},
{
"aimid": [
"4568999",
"6789345"
]
}]
and This is my Pojo class:-
#JsonProperty("aimid")
private String aimid;
public String getAimid() {
return aimid;
}
public void setAimid(String aimid) {
this.aimid = aimid;
}
I want to store aimid in pojo . When i am writing like above in my application i am getting error.
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_ARRAY token.
From my understanding i am getting error because of Array element so anyone can suggest me how i can capture both thing if it is coming as String or It is coming as a Array String
The challenge is that in some cases "aimid" is a string value but in another case it is an array.
If you have control over the structure of the JSON then update the structure so that each element of the root array has ONE of the following structures:
String
{
"aimid": "333674"
}
OR array
{
"aimid": [
"4568999",
"6789345"
]
}
If you do not have control of the structure of the data you will need to parse it yourself and process it into your POJO.
Please see these 3 code examples that should illustrate how you can go about this approaches. :
public class MyPojo {
private List<String> aimid;
#JsonProperty("aimid")
public List<String> getAimid() {
return aimid;
}
#JsonProperty("aimid_array")
public void setAimid(final List<String> aimid) {
this.aimid = aimid;
}
#JsonProperty("aimid")
public void setAimid(final String aimid) {
this.aimid = Arrays.asList(aimid);
}
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.*;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Test;
public class UnitTest {
private static final Logger LOGGER = Logger.getLogger(UnitTest.class.getName());
public UnitTest() {
}
#Test
public void testOneAimId() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid\": \"12345\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": \"333674\"\n"
+ "}]";
final List<MyPojo> result = new ObjectMapper().readValue(json, new TypeReference<List<MyPojo>>() {
});
log(Level.SEVERE, LOGGER, "testOneAimId", result);
}
#Test
public void testListAimIds() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid_array\": [\n" // HERE WE HAVE CHANGED THE JSON PROP NAME
+ " \"4568999\",\n"
+ " \"6789345\"\n"
+ " ]\n"
+ "}]";
final List<MyPojo> result = new ObjectMapper().readValue(json, new TypeReference<List<MyPojo>>() {
});
log(Level.SEVERE, LOGGER, "testListAimIds", result);
}
#Test
public void testMixed() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid\": \"12345\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": \"333674\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid_array\": [\n" // HERE WE HAVE CHANGED THE JSON PROP NAME
+ " \"4568999\",\n"
+ " \"6789345\"\n"
+ " ]\n"
+ "}]";
final List<MyPojo> result = new ObjectMapper().readValue(json, new TypeReference<List<MyPojo>>() {
});
log(Level.SEVERE, LOGGER, "testMixed", result);
}
#Test
public void testMixed2() throws IOException {
final String json = "[\n"
+ "{\n"
+ " \"aimid\": \"12345\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": \"333674\"\n"
+ "},\n"
+ "{\n"
+ " \"aimid\": [\n"
+ " \"4568999\",\n"
+ " \"6789345\"\n"
+ " ]\n"
+ "}]";
final JsonNode result = new ObjectMapper().readValue(json, JsonNode.class);
final ArrayList<String> arrayList = new ArrayList<>();
result.forEach((final JsonNode jsonNode) -> {
if (jsonNode.getNodeType() != JsonNodeType.OBJECT)
throw new IllegalArgumentException(jsonNode.toString());
final ObjectNode obj = (ObjectNode) jsonNode;
obj.forEach(o -> {
switch (o.getNodeType()) {
case ARRAY:
final ArrayNode array = (ArrayNode) o;
array.forEach(t -> arrayList.add(t.asText()));
break;
case STRING:
arrayList.add(o.asText());
break;
default:
throw new IllegalArgumentException(o.toString());
}
});
});
final MyPojo myPojo = new MyPojo();
myPojo.setAimid(arrayList);
log(Level.SEVERE, LOGGER, "myPojo", myPojo);
}
private void log(final Level level, final Logger logger, final String title, final Object obj) {
try {
if (title != null)
logger.log(level, title);
final ObjectWriter writer = new ObjectMapper().writerWithDefaultPrettyPrinter();
logger.log(level, obj == null ? "null" : writer.writeValueAsString(obj));
} catch (final JsonProcessingException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}

How to parse this XML SOAP response to a POJO?

I'm having a problem converting a XML SOAP return to a corresponding POJO class. The XML return looks like this:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header></env:Header>
<env:Body>
<ns2:teste xmlns:ns2="http://teste.com.br/">
<retorno>
<codigoRetorno>000</codigoRetorno>
<descricao>Consulta Realizada com Sucesso</descricao>
<item>
<a>teste</a>
<b>teste</b>
<c>teste</c>
</item>
<item>
<a>teste</a>
<b>teste</b>
<c>teste</c>
</item>
</retorno>
</ns2:teste >
</env:Body>
</env:Envelope>
I tried to use the Jackson XMLmapper, but I can not get it to consider the 'RETURN' node as the ROOT element during deserialization. it considers the 'Envelope' node as ROOT node.
I need to extract just the return node and convert to my pojo class.
Another problem is that 'item' nodes should be part of a collection, however there is no parent node grouping these elements.
Does anyone know of a parser that does deserialization of this type of xml?
You can incorporate a streaming XML Parser (StAX) and XmlMapper in this way:
import java.io.StringReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class Deser {
// #formatter:off
private static final String JSON = " <env:Envelope xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <env:Header></env:Header>\n" +
" <env:Body>\n" +
" <ns2:teste xmlns:ns2=\"http://teste.com.br/\">\n" +
" <retorno>\n" +
" <codigoRetorno>000</codigoRetorno>\n" +
" <descricao>Consulta Realizada com Sucesso</descricao>\n" +
" <item>\n" +
" <a>teste</a>\n" +
" <b>teste</b>\n" +
" <c>teste</c>\n" +
" </item>\n" +
" <item>\n" +
" <a>teste</a>\n" +
" <b>teste</b>\n" +
" <c>teste</c>\n" +
" </item>\n" +
" </retorno>\n" +
" </ns2:teste >\n" +
" </env:Body>\n" +
"</env:Envelope>";
// #formatter:on
private static final String TARGET_ELEMENT = "retorno";
public static void main(String[] args) throws Exception {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
XMLInputFactory f = XMLInputFactory.newFactory();
XMLStreamReader sr = f.createXMLStreamReader(new StringReader(JSON));
while (sr.hasNext()) {
int type = sr.next();
if (type == XMLStreamReader.START_ELEMENT && TARGET_ELEMENT.equals(sr.getLocalName())) {
Retorno r = xmlMapper.readValue(sr, Retorno.class);
System.out.println(r.getDescricao());
}
}
}
}
class Retorno {
private int codigoRetorno;
private String descricao;
public int getCodigoRetorno() {
return codigoRetorno;
}
public void setCodigoRetorno(int codigoRetorno) {
this.codigoRetorno = codigoRetorno;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
}
This yields:
Consulta Realizada com Sucesso
Adapt the code as necessary, this is just to prove out how to get it to do what you need!
The cleanest solution I found was using JSOUP:
private <T> T parseResponse(HttpEntity entity, Class<T> typeTarget) throws Exception {
try {
String xmlSoapResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8);
String xmlRetorno = extractXmlElement(xmlSoapResponse, "retorno");
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
xmlMapper.registerModule(new JavaTimeModule());
return xmlMapper.readValue(xmlRetorno.toString(), typeTarget);
} catch (Exception e) {
throw new Exception("Fail during parser", e);
}
}
private String extractXmlElement(String xmlString, String nodeTagNameElement) {
Document document = Jsoup.parse(xmlString, "", Parser.xmlParser());
document.outputSettings().prettyPrint(false);
Elements retorno = document.getElementsByTag(nodeTagNameElement);
return retorno.toString();
}

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:
Have I implemented the Future and Promise correctly?
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());
}
}
)
);
Maybe you have some great example that I have not found yet?
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()));
}
Although you have correctly implemented the Promise and Future, i wouldn't consider this code to be "non-blocking"...
It seems that the blocking call is
List<String> list = IndexService.find(term);
and although this is now wrapped in a promise/future, it is still a blocking call...
If you want to be truly non-blocking (with all its benefits), you'll have to make your data access (queries) non-blocking...
Oh, and a non-blocking action method should return a Promise of a Result, not a Result...
This is how i should write your code:
#BodyParser.Of(value = BodyParser.Json.class)
public static F.Promise<Result> complete(final String term) {
scala.concurrent.Future<List<String>> listFuture = IndexService.find(term);
F.Promise<List<String>> listPromise = F.Promise.wrap(listFuture);
return listPromise.map(new F.Function<List<String>, Result>() {
#Override
public Result apply(List<String> list) throws Throwable {
return ok(Json.toJson(list));
}
});
}
Hope this helps!

Categories