playframework-2.3 Akka-Async interaction porting? - java

I have some old playframework 2.2 java webservice that interacts with akka, and now I should port them to playframework 2.3.
However, async has been deprecated and even after reading the doc about the async porting (http://www.playframework.com/documentation/2.3.x/JavaAsync) I wasn't able to understand how to apply it to my case (code below):
I must make the await for a timeout/akka server reply before starting the construction of my reply (ok()), otherwise I will block the thread.
I should make the actorselection async too.
I should make the akka server reply parsing/reply construction async too
I looked around and I wasn't able to find an example of such interactions, even in typesafe templates.
How could I do that?
/* playframework 2.2 code */
public class Resolve extends Controller {
private final static String RESOLVER_ACTOR = play.Play.application().configuration().getString("actor.resolve");
#CorsRest
#VerboseRest
#RequireAuthentication
#BodyParser.Of(BodyParser.Json.class)
public static Result getJsonTree() {
JsonNode json = request().body().asJson();
ProtoBufMessages.ResolveRequest msg;
ResolveRequestInput input;
try {
input = new ResolveRequestInput(json);
} catch (rest.exceptions.MalformedInputException mie) {
return badRequest(mie.getMessage());
}
msg = ((ProtoBufMessages.ResolveRequest)input.getMessage());
ActorSelection resolver = Akka.system().actorSelection(RESOLVER_ACTOR);
Timeout tim = new Timeout(Duration.create(4, "seconds"));
Future<Object> fut = Patterns.ask(resolver, input.getMessage(), tim);
return async (
F.Promise.wrap(fut).map(
new F.Function<Object, Result>() {
public Result apply(Object response) {
ProtoBufMessages.ResolveReply rsp = ((ProtoBufMessages.ResolveReply)response);
ResolveOutput output = new ResolveOutput(rsp);
return ok(output.getJsonReply());
}
}
)
);
}
}

I came out with the code below
public class Resolve extends Controller {
private final static String RESOLVER_ACTOR = play.Play.application().configuration().getString("actor.resolve");
private final static BrainProtoMessages.ResolveReply request_error = BrainProtoMessages.ResolveReply.newBuilder()
.setReturnCode(BResults.REQUEST_FAILED)
.build();
#CorsRest
#VerboseRest
#RequireAuthentication
#BodyParser.Of(BodyParser.Json.class)
public static Result resolve_map() {
final ResolveRequestInput input;
final F.Promise<ActorSelection> selected_target;
final F.Promise<Future<Object>> backend_request;
final F.Promise<BrainProtoMessages.ResolveReply> backend_reply;
final F.Promise<ObjectNode> decode_json;
final F.Promise<Result> ok_result;
final JsonNode json = request().body().asJson();
try {
input = new ResolveRequestInput(json);
} catch (rest.exceptions.MalformedInputException mie) {
return badRequest(mie.getMessage());
}
selected_target = F.Promise.promise(
new F.Function0<ActorSelection>() {
#Override
public ActorSelection apply() throws Throwable {
return Akka.system().actorSelection(RESOLVER_ACTOR);
}
}
);
backend_request =
selected_target.map(
new F.Function<ActorSelection, Future<Object>>() {
#Override
public Future<Object> apply(ActorSelection actorSelection) throws Throwable {
return Patterns.ask(actorSelection, input.getMessage(),new Timeout(Duration.create(4, "seconds")));
}
}
);
backend_reply = backend_request.map(
new F.Function<Future<Object>, BrainProtoMessages.ResolveReply>() {
#Override
public BrainProtoMessages.ResolveReply apply(Future<Object> akka_reply) throws Throwable {
try {
return (BrainProtoMessages.ResolveReply) Await.result(akka_reply, Duration.create(4, "seconds"));
}catch(Exception error)
{
return request_error;
}
}
}
);
decode_json = backend_reply.map(
new F.Function<BrainProtoMessages.ResolveReply, ObjectNode>() {
#Override
public ObjectNode apply(BrainProtoMessages.ResolveReply response) throws Throwable {
return new ResolveOutput(response).getJsonReply();
}
}
);
ok_result = decode_json.map(
new F.Function<ObjectNode, Result>() {
#Override
public Result apply(ObjectNode reply) {
return ok(reply);
}
}
);
try {
return ok_result.get(8000);
}catch(Exception error)
{
return internalServerError();
}
}
}

Related

loadInitial method not getting called in PositionalDataSource<Item>

I'm implementing PositionalDataSource from Paging Library in Java and getting an issue that constructor of PositionalDataSource's child class is getting called but after that loadInitial method is not getting called.
public HistoryPositionalDataSource(List<CallTable> callLogs)
{
this.callLogs = callLogs;
Log.d("PaginationDataSource", "Constructor");
}
#Override
public void loadInitial(#NonNull LoadInitialParams params, #NonNull LoadInitialCallback callback) {
Log.d("PaginationDataSource", "loadInitial");
if (callLogs!=null && !callLogs.isEmpty())
{
int totalCount = computeCount();
int position = computeInitialLoadPosition(params, totalCount);
int loadSize = computeInitialLoadSize(params, position, totalCount);
callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
}
}
#Override
public void loadRange(#NonNull LoadRangeParams params, #NonNull LoadRangeCallback callback) {
callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
}
Here's my PageListConfig
private void init() {
pagedListConfig = (new PagedList.Config.Builder()).setEnablePlaceholders(true)
.setInitialLoadSizeHint(Integer.MAX_VALUE).setPageSize(Integer.MAX_VALUE).build();
Executor executor = Executors.newFixedThreadPool(3);
List<CallTable> listLogs = getCallLogs(context);
historyDataSourceFactory = new HistoryDataSourceFactory(listLogs);
LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(historyDataSourceFactory, pagedListConfig);
pagedCallLogs = livePagedListBuilder
.setFetchExecutor(executor)
.build();
}
Factory class:
public class HistoryDataSourceFactory extends DataSource.Factory {
private static final String TAG = HistoryDataSourceFactory.class.getSimpleName();
private HistoryPositionalDataSource historyPositionalDataSource;
public HistoryDataSourceFactory(List<CallTable> callLogs)
{
if (callLogs!=null && !callLogs.isEmpty())
{
Log.d("PaginationFactory", "NotNullLogs");
historyPositionalDataSource = new HistoryPositionalDataSource(callLogs);
}
}
#Override
public DataSource create() {
return historyPositionalDataSource;
}
}
My getPagedCallLogs method:
public synchronized LiveData<PagedList<CallTable>> getPagedCallLogs() {
if (pagedCallLogs!=null && pagedCallLogs.getValue()!=null)
{
Log.d("PagingGetData", "Done");
return pagedCallLogs;
}
else
{
Log.d("PagingGetData", "Null");
return null;
}
}
Logs image is given below.
Load size and offset is set
via PagedList.Config so you don't need to calculate load range yourself.
Change your loadInitial function
#Override
public void loadInitial(#NonNull LoadInitialParams params, #NonNull LoadInitialCallback callback) {
Log.d("PaginationDataSource", "loadInitial");
if (callLogs!=null && !callLogs.isEmpty())
{
callback.onResult(loadRangeInternal(0, params.requestedLoadSize), 0);
}
}
Edit:
Try this config aswell
PagedList.Config config =
new PagedList.Config.Builder()
.setPageSize(50)
.setEnablePlaceholders(false)
.setPrefetchDistance(25)
.build();
Edit2:
Try changing extention from DataSource.Factory to DataSource.Factory<Integer, ModelClass> and PositionalDataSource to PositionalDataSource<ModelClass>
After too many struggle, I become able to resolve my issue. The issue was with my getPagedCallLog method.
I wrote:
public synchronized LiveData<PagedList<CallTable>> getPagedCallLogs() {
if (pagedCallLogs!=null && pagedCallLogs.getValue()!=null)
{
Log.d("PagingGetData", "Done");
return pagedCallLogs;
}
else
{
Log.d("PagingGetData", "Null");
return null;
}
}
I was taking Google I/O '18, in which he said that loadInitial is called by the pageList, then I realise that it wasn't getting called in my case. And it is working fine after removing pagedCallLogs.getValue()!=null which was my stupid mistake.
Now it looks like this:
public synchronized LiveData<PagedList<CallTable>> getPagedCallLogs() {
if (pagedCallLogs!=null)
{
Log.d("PagingGetData", "Done");
return pagedCallLogs;
}
else
{
Log.d("PagingGetData", "Null");
return null;
}
}

Unit testing respository with in MVVM pattern android

I am trying to write unit tests for repository while using MVVM pattern in android.
What i have is a repository which fetched data from the network using retrofit
public class ValidateCbuRepository {
private static ValidateCbuRepository single_instance = null;
private MutableLiveData<CBUValidationImageResponse> data = new MutableLiveData<>();
public static ValidateCbuRepository getInstance() {
if (single_instance == null)
single_instance = new ValidateCbuRepository();
return single_instance;
}
public MutableLiveData<CBUValidationImageResponse> processImage(String encodedString) {
JsonObject postParam = new JsonObject();
postParam.addProperty("image", encodedString);
Api service = RetrofitClientInstance.getRetrofitInstance().create(Api.class);
data.setValue(null);
HttpUrl httpUrl = HttpUrl.parse("some url");
Call<CBUValidationImageResponse> responseCall = service.getProcessedImage_cbu_validation(httpUrl.toString(),postParam);
responseCall.enqueue(new Callback<CBUValidationImageResponse>() {
#Override
public void onResponse(Call<CBUValidationImageResponse> call, Response<CBUValidationImageResponse> response) {
if(response.isSuccessful()) {
CBUValidationImageResponse res = response.body();
CBUValidationImageResponse cbuValidationImageResponse = res;
Log.i("CBU response ",""+cbuValidationImageResponse.toString());
cbuValidationImageResponse.setSuccess(true);
cbuValidationImageResponse.setShowProgres(false);
cbuValidationImageResponse.setError(false);
data.setValue(cbuValidationImageResponse);
}
}
#Override
public void onFailure(Call<CBUValidationImageResponse> call, Throwable t) {
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setError(true);
cbuValidationImageResponse.setShowProgres(false);
data.setValue(cbuValidationImageResponse);
t.printStackTrace();
}
});
return data;
}
}
The unit test part
#Mock
private Observer<CBUValidationImageResponse> observer;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testApiResponse_success() {
Api mockedApiInterface = Mockito.mock(Api.class);
Call<CBUValidationImageResponse> mockedCall = Mockito.mock(Call.class);
Mockito.when(mockedApiInterface.getProcessedImage_cbu_validation(any(),any())).thenReturn(mockedCall);
try {
Mockito.doAnswer(new Answer() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Callback<CBUValidationImageResponse> callback = invocation.getArgument(0);
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setCBU_code("some code");
cbuValidationImageResponse.setHeight(7);
cbuValidationImageResponse.setBreadth(7);
cbuValidationImageResponse.setLength(7);
callback.onResponse(mockedCall, Response.success(cbuValidationImageResponse));
// or callback.onResponse(mockedCall, Response.error(404. ...);
// or callback.onFailure(mockedCall, new IOException());
return null;
}
}).when(mockedCall).enqueue(any(Callback.class));
ValidateCbuRepository validateCbuRepository = new ValidateCbuRepository();
String encodedString= "";
validateCbuRepository.processImage(encodedString).observeForever(observer);
Getting a null pointer exception at validateCbuRepository.processImage(encodedString).observeForever(observer). Next step is to verify the observer.
I expect the test to pass. What am i doing wrong here?. I did something similar foe view model and the test passes with 100% code coverage.
The retrofit call is asynchronous. Is that the reason why it fails?
Edit : It seems livedata is null while testing causing NPE.

Can I clean the value of the map in the Netty's ChannelHandler with a periodic timer task?

I define a schedule task in the main thread, I want to clean the value of the map which caches my project's client information in ChannelHandler. But it doesn't work. What did I do wrong?
This is main app code where I schedule a task.
public class Server {
public static void main(String[] args) throws Exception {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
ServerBootstrap b = new ServerBootstrap();
//init() code Omitted
ScheduledFuture<?> sf = ctx.executor().scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
HashSet<String> clients = new HashSet<>();
Map<String,String> map = LoginAuthRespHandler.getNodeCheck();
System.out.println(map.size());
for (String key:map.keySet()) {
clients.add(map.get(key));
}
try{
//doSomething();
}catch (Exception e){
e.printStackTrace();
}
map.clear();
clients.clear();
}
},10,10,TimeUnit.SECONDS);
ChannelFuture cf = b.bind(NettyConstant.REMOTEIP,NettyConstant.PORT).sync();
System.out.println("Netty server start ok on: "
+ (NettyConstant.REMOTEIP + " : " + NettyConstant.PORT));
cf.channel().closeFuture().sync();
work.shutdownGracefully();
boss.shutdownGracefully();
}
}
And this is the ChannelHandler code.
public class LoginAuthRespHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginAuthRespHandler.class);
private static Map<String, String> nodeCheck = new ConcurrentHashMap<String, String>();
private String[] whiteList = { "127.0.0.1", "192.168.56.1" };
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
AlarmMessage message = (AlarmMessage) msg;
if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_REQ.value()) {
String nodeIndex = ctx.channel().remoteAddress().toString();
AlarmMessage loginResp = null;
if (nodeCheck.containsKey(nodeIndex)) {
loginResp = buildResponse(ResultType.FAIL);
} else {
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
String ip = address.getAddress().getHostAddress();
boolean isOK = false;
for (String WIP : whiteList) {
if (WIP.equals(ip)) {
isOK = true;
break;
}
}
loginResp = isOK ? buildResponse(ResultType.SUCCESS) : buildResponse(ResultType.FAIL);
if (isOK)
//add a client value to the map
nodeCheck.put(nodeIndex, message.getBody().toString());
}
ctx.writeAndFlush(loginResp);
} else {
ctx.fireChannelRead(msg);
}
}
private AlarmMessage buildResponse(ResultType result) {
AlarmMessage message = new AlarmMessage();
Header header = new Header();
header.setType(MessageType.LOGIN_RESP.value());
message.setHeader(header);
message.setBody(result.value());
return message;
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String nodeIndex = ctx.channel().remoteAddress().toString();
ctx.close();
if(nodeCheck.containsKey(nodeIndex)){
nodeCheck.remove(nodeIndex);
}
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
//nodeCheck.remove(ctx.channel().remoteAddress().toString());
ctx.close();
ctx.fireExceptionCaught(cause);
}
public synchronized static Map<String, String> getNodeCheck() {
return nodeCheck;
}
}

How to use custom converter?

I want to parse time from
<monday>
<item>
<time>00:00:00</time>
</item>
...
</monday>
as long
I defiend items as
#Root(strict = false)
private static class Item {
#Element(name = "time")
#Convert(TimeConverter.class)
private Long time;
}
My Converter
public class TimeConverter implements org.simpleframework.xml.convert.Converter<Long> {
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
#Override
public Long read(InputNode node) throws Exception {
try {
String value = node.getValue();
return df.parse(value).getTime();
} catch (Exception e) {
Log.e("mcheck", "read: ", e);
return 0L;
}
}
#Override
public void write(OutputNode node, Long value) throws Exception {
try {
String v = df.format(new Date(value));
node.setValue(v);
} catch (Exception e) {
Log.e("mcheck", "write: ", e);
}
}
}
However when I parse it I receive
retrofit.RetrofitError: java.lang.NumberFormatException: Invalid long: "00:00:00"
As this exception is not caught in my try-catch blocks in converter I assume that parser does not visit converter at all.
My retrofit 1.9 call
OkHttpClient httpClient = new OkHttpClient();
RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setEndpoint(url);
builder.setLogLevel(RestAdapter.LogLevel.FULL);
builder.setConverter(new SimpleXMLConverter());
builder.setClient(new OkClient(httpClient));
RestAdapter restAdapter = builder.build();
ChansonApi api = restAdapter.create(ChansonApi.class);
api.getStreamProgram(new Callback<StreamProgram>() {
#Override
public void success(StreamProgram streamProgram, Response response) {
if(streamProgram!=null){
Log.e("mcheck", "success: "+streamProgram.getProgram());
}
}
#Override
public void failure(RetrofitError error) {
Log.e("mcheck", "failure: ",error);
}
});
The problem was in proguard settings. I had to add TimeConverter to -keep class list

Need ideas on fixing RSS feed parsing

http://www.ibm.com/developerworks/opensource/library/x-android/
I am using the code here, specifically the AndroidSaxParser. The problem is, is that I get all 4 parts of the Message objects the same as the title. I've combed it over and over, but I can't find anything wrong with what I put together.
Any ideas on where to look?
Here is the code:
public class AndroidSaxFeedParser extends BaseFeedParser {
public AndroidSaxFeedParser(String feedUrl) {
super(feedUrl);
}
public List<Message> parse() {
final Message currentMessage = new Message();
RootElement root = new RootElement("rss");
final List<Message> messages = new ArrayList<Message>();
Element channel = root.getChild("channel");
Element item = channel.getChild(ITEM);
item.setEndElementListener(new EndElementListener(){
public void end() {
messages.add(currentMessage.copy());
}
});
item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setTitle(body);
}
});
item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setLink(body);
}
});
item.getChild(DESCRIPTION).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setDescription(body);
}
});
item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setDate(body);
}
});
try {
Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
} catch (Exception e) {
throw new RuntimeException(e);
}
return messages;
}
}
public abstract class BaseFeedParser implements FeedParser {
// names of the XML tags
static final String PUB_DATE = "pubDate";
static final String DESCRIPTION = "description";
static final String LINK = "link";
static final String TITLE = "title";
static final String ITEM = "item";
static final String CHANNEL = "channel";
final URL feedUrl;
protected BaseFeedParser(String feedUrl){
try {
this.feedUrl = new URL(feedUrl);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
protected InputStream getInputStream() {
try {
return feedUrl.openConnection().getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

Categories

Resources