<file_sep>package controller.page;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import common.ActorUtil;
import facade.actor.ArticleFacadeActor;
import facade.message.ArticleFacadeFindAllRequest;
import facade.message.ArticleFacadeFindAllResponse;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import javax.inject.Singleton;
import play.api.inject.Injector;
import play.mvc.Controller;
import play.mvc.Result;
import view.html.home;

@Singleton
public class HomeController extends Controller {
    private final ActorRef articleFacadeActor;

    @Inject
    public HomeController(ActorSystem actorSystem, Injector injector) {
        articleFacadeActor = actorSystem.actorOf(ActorUtil.props(ArticleFacadeActor.class, injector));
    }

    public CompletionStage<Result> renderHomePage() {
        CompletionStage<ArticleFacadeFindAllResponse> futureResponse = ActorUtil.ask(articleFacadeActor, new ArticleFacadeFindAllRequest());
        return futureResponse.thenApply(response -> ok(home.render(response.getDtos())));
    }
}
<file_sep>package dao.message;

public class ArticleDaoFindAllRequest {
}
<file_sep>package dao.message;

import model.ArticleModel;

import java.util.Collection;

public class ArticleDaoFindAllResponse {
    private final Collection<ArticleModel> models;

    public ArticleDaoFindAllResponse(Collection<ArticleModel> models) {
        this.models = models;
    }

    public Collection<ArticleModel> getModels() {
        return models;
    }
}
<file_sep>package dao.message;

import model.ArticleModel;

public class ArticleDaoUpdateRequest {
    private final ArticleModel model;

    public ArticleDaoUpdateRequest(ArticleModel model) {
        this.model = model;
    }

    public ArticleModel getModel() {
        return model;
    }
}
<file_sep>package service.message;

import model.ArticleModel;

import java.util.Collection;

public class ArticleServiceFindAllResponse {
	private final Collection<ArticleModel> models;

    public ArticleServiceFindAllResponse(Collection<ArticleModel> models) {
        this.models = models;
    }

    public Collection<ArticleModel> getModels() {
        return models;
    }
}
<file_sep>package facade.converter;

import common.BiConverter;
import dto.ArticleDto;
import model.ArticleModel;

import javax.inject.Singleton;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

@Singleton
public class ArticleModelDtoBiConverter extends BiConverter<ArticleModel, ArticleDto> {
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT);

    @Override
    public ArticleDto forth(ArticleModel item) {
        ArticleDto dto = new ArticleDto();
        dto.setId(item.getId());
        dto.setTitle(item.getTitle());
        dto.setDescription(item.getDescription());
        dto.setDate(FORMATTER.format(item.getDate()));
        dto.setContent(item.getContent());
        return dto;
    }

    @Override
    public ArticleModel back(ArticleDto item) {
        return new ArticleModel(item.getId(), item.getTitle(), item.getDescription(), LocalDateTime.now(), item.getContent());
    }
}
<file_sep>package controller.common;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import common.ActorUtil;
import common.Converter;
import controller.form.ArticleForm;
import dto.ArticleDto;
import facade.actor.ArticleFacadeActor;
import facade.message.ArticleFacadeCreateRequest;
import facade.message.ArticleFacadeDeleteRequest;
import facade.message.ArticleFacadeUpdateRequest;
import javax.inject.Inject;
import javax.inject.Singleton;
import play.api.inject.Injector;
import play.data.Form;
import play.data.FormFactory;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Security;

@Singleton
public class ArticleController extends Controller {
    @Inject
    private FormFactory formFactory;

    @Inject
    private Converter<ArticleForm, ArticleDto> articleFormConverter;

    private final ActorRef articleFacadeActor;

    @Inject
    public ArticleController(ActorSystem actorSystem, Injector injector) {
        articleFacadeActor = actorSystem.actorOf(ActorUtil.props(ArticleFacadeActor.class, injector));
    }

    @Security.Authenticated
    public Result handleUpdateArticle(String id) {
        Form<ArticleForm> form = formFactory.form(ArticleForm.class).bindFromRequest();
        if (form.hasErrors()) {
            return badRequest();
        } else {
            ArticleDto dto = articleFormConverter.forth(form.get());
            dto.setId(id);
            articleFacadeActor.tell(new ArticleFacadeUpdateRequest(dto), articleFacadeActor);
            return ok();
        }
    }

    @Security.Authenticated
    public Result handleCreateArticle() {
        Form<ArticleForm> form = formFactory.form(ArticleForm.class).bindFromRequest();
        if (form.hasErrors()) {
            return badRequest();
        } else {
            articleFacadeActor.tell(new ArticleFacadeCreateRequest(articleFormConverter.forth(form.get())), articleFacadeActor);
            return ok();
        }
    }

    @Security.Authenticated
    public Result handleDeleteArticle(String id) {
        articleFacadeActor.tell(new ArticleFacadeDeleteRequest(id), articleFacadeActor);
        return ok();
    }
}
<file_sep>package dao.message;

import model.UserModel;

public class UserDaoFindResponse {
    private final UserModel model;

    public UserDaoFindResponse(UserModel model) {
        this.model = model;
    }

    public UserModel getModel() {
        return model;
    }
}
<file_sep>docstoreonly=true

docstore.url=http://127.0.0.1:9200
docstore.active=true
docstore.generateMapping=false
docstore.dropCreate=false
docstore.create=true<file_sep>import * as PR from 'google-code-prettify/bin/prettify.min.js';

const BROWSER_UNSUPPORTED_METHODS: Array<String> = ["PUT", "DELETE"];

function sendRequest(method: string, url: string, data: any): void {
    let xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.send(data);
}

function handleUnsupportedFormMethods(): void {
    let forms = document.getElementsByTagName("form");
    for (let i = 0; i < forms.length; i++) {
        let form = forms[i];
        let method = form.getAttribute("method");
        if (BROWSER_UNSUPPORTED_METHODS.indexOf(method) != -1) {
            form.onsubmit = (event: Event) => {
                event.preventDefault();
                sendRequest(method, form.action, new FormData(form));
            }
        }
    }
}

window.onload = () => {
    PR.prettyPrint();
    handleUnsupportedFormMethods();
};<file_sep>package common;

import akka.util.Timeout;
import scala.concurrent.duration.Duration;

import java.util.concurrent.TimeUnit;

public final class ActorConstant {
	public static final Timeout MAX_PATTERN_ASK_TIMEOUT = Timeout.apply(Duration.apply(30, TimeUnit.SECONDS));

	private ActorConstant() {}
}
<file_sep>package dao;

import model.ArticleModel;

import java.util.List;

public interface ArticleDao {
    List<ArticleModel> findAll();

    ArticleModel find(String id);

    void create(ArticleModel model);

    void update(ArticleModel model);

    void delete(String id);
}
<file_sep>package dao.message;

import model.ArticleModel;

public class ArticleDaoCreateRequest {
    private final ArticleModel model;

    public ArticleDaoCreateRequest(ArticleModel model) {
        this.model = model;
    }

    public ArticleModel getModel() {
        return model;
    }
}
<file_sep>package service;

import javax.inject.Singleton;

@Singleton
public class ArticleService {
}
<file_sep>package dao.actor;

import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;
import dao.ArticleDao;
import dao.message.ArticleDaoCreateRequest;
import dao.message.ArticleDaoDeleteRequest;
import dao.message.ArticleDaoFindAllRequest;
import dao.message.ArticleDaoFindAllResponse;
import dao.message.ArticleDaoFindRequest;
import dao.message.ArticleDaoFindResponse;
import dao.message.ArticleDaoUpdateRequest;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ArticleDaoActor extends AbstractActor {
    private static final Logger LOGGER = LoggerFactory.getLogger(ArticleDaoActor.class);

    private final ArticleDao articleDao;

    @Inject
    public ArticleDaoActor(ArticleDao articleDao) {
        this.articleDao = articleDao;

        receive(
            ReceiveBuilder
                .match(ArticleDaoFindAllRequest.class, this::findAll)
                .match(ArticleDaoFindRequest.class, this::find)
                .match(ArticleDaoCreateRequest.class, this::add)
                .match(ArticleDaoUpdateRequest.class, this::update)
                .match(ArticleDaoDeleteRequest.class, this::delete)
                .matchAny(message -> LOGGER.warn("Received unknown message: {}", message))
                .build()
        );
    }

    private void findAll(ArticleDaoFindAllRequest ignored) {
        sender().tell(new ArticleDaoFindAllResponse(articleDao.findAll()), self());
    }

    private void find(ArticleDaoFindRequest message) {
        sender().tell(new ArticleDaoFindResponse(articleDao.find(message.getId())), self());
    }

    private void add(ArticleDaoCreateRequest message) {
        articleDao.create(message.getModel());
    }

    private void update(ArticleDaoUpdateRequest message) {
        articleDao.update(message.getModel());
    }
    
    private void delete(ArticleDaoDeleteRequest message) {
        articleDao.delete(message.getId());
    }
}
<file_sep>package controller.page;

import play.mvc.Controller;
import play.mvc.Result;
import view.admin.html.login;

public class LoginController extends Controller {
    public Result renderLoginPage() {
        return ok(login.render());
    }
}
<file_sep># Notes
Simple note manager<file_sep>package common;

import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;

public final class ConcurrencyUtil {
	private ConcurrencyUtil() {}

	public static <R> R blocked(CompletionStage<R> stage) {
		try {
			return stage.toCompletableFuture().get();
		} catch (InterruptedException | ExecutionException e) {
			throw new IllegalStateException(e);
		}
	}
}
<file_sep>package dao.ebean;

import dao.ArticleDao;
import io.ebean.Ebean;
import java.util.List;
import javax.inject.Singleton;
import model.ArticleModel;

@Singleton
public class ArticleEbeanDao implements ArticleDao {

    @Override
    public List<ArticleModel> findAll() {
        return Ebean.find(ArticleModel.class)
                    .findList();
    }

    @Override
    public ArticleModel find(String id) {
        return Ebean.find(ArticleModel.class)
                    .setId(id)
                    .findUnique();
    }

    @Override
    public void create(ArticleModel model) {
        Ebean.save(model);
    }

    @Override
    public void update(ArticleModel model) {
        Ebean.update(model);
    }

    @Override
    public void delete(String id) {
        Ebean.delete(find(id));
    }
}
<file_sep>package service.actor;

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.japi.pf.ReceiveBuilder;
import akka.pattern.PatternsCS;
import common.ActorUtil;
import dao.actor.UserDaoActor;
import dao.message.UserDaoFindRequest;
import dao.message.UserDaoFindResponse;
import java.util.concurrent.CompletionStage;
import javax.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.api.inject.Injector;
import service.UserService;
import service.message.UserServiceAuthRequest;
import service.message.UserServiceAuthResponse;

public class UserServiceActor extends AbstractActor {
	private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceActor.class);

	private final UserService service;

	private final ActorRef UserDaoActor;

	@Inject
	public UserServiceActor(Injector injector, UserService service) {
		this.service = service;

		UserDaoActor = context().actorOf(ActorUtil.props(UserDaoActor.class, injector));

		receive(
			ReceiveBuilder
				.match(UserServiceAuthRequest.class, this::authenticate)
				.matchAny(message -> LOGGER.warn("Received unknown message: {}", message))
				.build()
		);
	}

	private void authenticate(UserServiceAuthRequest message) {
		CompletionStage<UserDaoFindResponse> future = ActorUtil.ask(UserDaoActor, new UserDaoFindRequest(message.getModel().getUsername()));
		PatternsCS.pipe(
			future.thenApply(result -> new UserServiceAuthResponse(service.authenticate(result.getModel(), message.getModel()))),
			context().dispatcher()
		).to(sender(), self());
	}
}
<file_sep>package facade;

import common.BiConverter;
import dto.ArticleDto;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import model.ArticleModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class ArticleFacade {
    private static final Logger LOGGER = LoggerFactory.getLogger(ArticleFacade.class);

    @Inject
    private BiConverter<ArticleModel, ArticleDto> converter;

    public ArticleModel convertBack(ArticleDto dto) {
        ArticleModel model = converter.back(dto);
        LOGGER.debug("Dto={} has been converted to model={}", dto, model);
        return model;
    }

    public ArticleDto convertForth(ArticleModel model) {
        ArticleDto dto = converter.forth(model);
        LOGGER.debug("Model={} has been converted to dto={}", model, dto);
        return dto;
    }

    public Collection<ArticleDto> convertForth(Collection<ArticleModel> models) {
        Collection<ArticleDto> dtos = converter.forth(models);
        LOGGER.debug("Models={} has been converted to dtos={}", models, dtos);
        return dtos;
    }
}
<file_sep>package common;

import java.util.Collection;
import java.util.stream.Collectors;

public abstract class Converter<F, T> {
    public abstract T forth(F item);

    public Collection<T> forth(Collection<F> items) {
        return items.stream().map(this::forth).collect(Collectors.toList());
    }
}
<file_sep>package service.message;

public class ArticleServiceFindAllRequest {
}
