Organizing composable Play apps with Flexbox

Yevgeniy Brikman made a awesome presentation about how Linkedin solved its problems to build complex web pages. The basic problem is that, for instance, your profile’s page is too complex and keep one single file like a jsp, to build all this html, is not maintainable. Obviously, we are thinking in a long running app.
There are a lot of objects that have to be loaded to create that page. For example:

  • Basic information like: name, current company, etc.
  • Background information
  • Skills and Endorsements
  • Education
  • People also viewed

This is a major problem for all big web sites and each one has its own version of solution. A lot of them, load the snippets of page from iframes and with this, they can load these pieces from everywhere they want… Another approach is when the server sends a almost blank page and every snippet is loaded from ajax calls. Both approaches solve the problem but it delegates a lot of responsibility to the browser.

The third possibility, which is more difficult, would be load all snippets in your server side and glue them to build the requested page. Using this approach, even in a synchronous way, you would have just small pages that are more testable, easier to maintain and would send to browser just a simple html. Facebook engineers wrote about that in this amazing post.

Linkedin guys used this same approach to change the way them build web pages. They used Play’s API to fetch small pieces of html and aggregate these snippets to compose the page. But if you remember, Play is all about async and streaming data. So Linkedin went ahead and used this power to fetch all pagelets in async way, using the Promise’s API provided by Play. Now, and if the last Promise completes its job before the first one, what your controller would have to do? Wait for the first? The answer is no! If you have a partial of your html, just send to the browser :). To accomplish this task, they used the Iteratee and Enumerator API to stream each html partial as soon as it is available. This project is hosted on github(link aqui) and I advise you to take a serious look!.

Everything is really cool until now, but how they organize all pagelets to build the page? I mean, there is a order between Background information’s block and Education’s block . They solved this using a simple Javascript snippet inside pagelet’s template. Take a look:

    @(contents: Html, id: String)

    <script type="text/html-stream" id="@id-contents">
      @contents
    </script>

    <script type="text/javascript">
      document.getElementById("@id").innerHTML = document.getElementById("@id-contents").innerHTML;
    </script>

Each pagelet knows what is its id in the main template. So, it is used javascript to put the returned html in the correct place, smart :). Talking with @sergio_caelum and @srsaude, masters in frontend topic, they warned me about the high cost that is to use innerHtml in your browser. Especially if you have to use it a lot of times! So they proposed to me that would be interesting if the Linkedin had used the Flexbox property in CSS3. This property, allows you rearrange every element in your page with CSS, no Javascript is needed. I liked the idea so I forked the code and I made this little change.

Now, each snippet has its own div and I just have to define the appearance order in the CSS file :).

    @(wvypCount: Int, searchCount: Int)

    <div class="wvyp">
      <h2>Who's Viewed Your Profile</h2>

      @views.html.wvyp.wvypCount(wvypCount)
      @views.html.wvyp.searchCount(searchCount)
    </div>

And now the css code:

    .wvyp {
    	display: -webkit-flex;
    	display: flex;
    	-webkit-flex-flow: column;
    	 flex-flow: column;
    }

    .wvyp .wvyp-count {
    	color: #0077b5;
    	-webkit-order: 1;
    	 order: 1;
    }

    .wvyp .search-count {
    	color: #333;
    	-webkit-order: 2;
    	order: 2;
    }

Now, I can even delete the pagelet template file :). This changed version is in my repository, go there and take a look!.

I really like this idea and I wish to have a chance to apply this solution in one of the systems that I am helping to build. What did you think? I had to write this post because this presentation is one of the best that I ever saw :). I don’t remember when I spent 50 minutes watching a video!

 

 

 

Advertisements

Play 2.3, Java 8 and async results

For the Java developers, Play 2.3 greatest feature is the native support for Java 8 and all things that are possible with lamba support.  Almost all pieces of code that were built with anonymous classes now can be replaced with lambdas.

Let’s see the case when we want to return a async result. The older version is like this:

    Promise<List<Photo>> promise = Promise.promise(new Function0<List<Photo>>() {

    	@Override
    	public List<Photo> apply() throws Throwable {
  		  return JPA.em().createQuery("select p from Photo p",Photo.class)
  		  .getResultList();
    	}
    });

    return promise.map(new Function<List<Photo>, Result>() {

    	@Override
    	public Result apply(List<Photo> photos) throws Throwable {
    		Html render = views.html.photos.render(photos);
    		return ok(render);
    	}
    });

The new version with lambdas is way more cleaner.

    Promise<List<Photo>> listingPhotos = Promise.promise( () -> {
    	return JPA.em().createQuery("select p from Photo",Photo.class).getResultList();
    });

Remember, all methods that receive functional interfaces(interfaces with just one method) can be used passing lambda as arguments!

And here, we didn’t separate our code like we should. Database access is being done in our controller. If we put this code in a different class, we can improve the usage of promise.

  Promise<List<Photo>> listingPhotos = Promise.promise(Photos::all);

Method Reference is a great feature to use with lambdas, the code is much more cleaner. Now you have to use map method to transform the Promise<List<Photo>>  to Promise<Result>.  This will be a common situation around your application.

    return listingPhotos.map((photos) -> {
    	Html render = views.html.photos.render(photos);
    	return ok(render);
    });

Here we can try to be more creative. I would like to use Method Reference to create Promise<Result> rather than use the map method. It would be nice if we had used a pimped version of Promise class with statuses map method. Here a example:

    return new PimpedPromise<List<Photo>>(listingPhotos).
            map(Controller::ok,views.html.photos::render);

There is nothing out of the box in Play so, to accomplish this, we can create our pimped version! Take a look:

    public class PimpedPromise<T> {

    	private Promise<T> promise;

    	public PimpedPromise(Promise<T> promise) {
    		this.promise = promise;
    	}

    	public <B extends Content> Promise<Result> map(play.libs.F.Function<Content, play.mvc.Results.Status> statusFunction,Function<? super T, B> callback) {
    		Promise<B> contentPromise = promise.map(callback);
    		return contentPromise.map(content -> {
    			return statusFunction.apply(content);
    		});
    	}

    }

We just changed a little bit the signature of map method :). We forced the developer to pass a function(second argument) that returns a Content object. Then we get the result of promise.map and we have to map again using the status function(first argument). Maybe it is a little bit complicated inside our pimped promise but is, at least for me, better to be used in controllers.

What do you think? Have you tried to create something new with the lambda support?

Multi tenant approach with Java 8 in a Play project.

In my current project, I had to implement a multi tenant approach. There is nothing new about this, I have to check if current user is the owner of some resource. And I have to do the same thing across other several actions. The best way to do this, is to build an Action composition and compose all actions with it. So, let’s say we have this route:

   /admin/users/update/:id

Now, when we try to access this resource, our interceptor will be invoked:

    @Override
    public Promise<SimpleResult> call(Context context) throws Throwable {
    	RoleType currentRole = CurrentUser.role();

    	Long currentLoggedUserId = CurrentUser.id();
    	Long clientParamId = //get parameter here;
    	for (RoleType roleType : rolesAllowed) {
    		if(roleType.acceptsRole(currentRole)
    		&& roleType.acceptsTenant(currentLoggedUserId,clientParamId)){
    			try {
    				return delegate.call(context);
    			} catch (Throwable e) {
    				throw new RuntimeException(e);
    			}
    		}
    	}
    	return Promise.<SimpleResult>pure(
    	  Controller.unauthorized("You are not allowed to see this page"));
    }

And now we have a problem. How do we extract the client id from the url? The first thing that I thought was: All parameters are in request data…

  Long clientParamId = Long.parseLong(context.request()
                     .queryString().get("clientId")[0]);

Unfortunately, Play does not provide this information anywhere :(, if you try to run this code, you will get a NPE. This probably happens because in Scala version you would not have this problem, in there you compose actions in a different way. Basically you can invoke the “call” method from your controller’s action, using the functional support provided by the language instead of using annotation. You can see more here(link). To solve my problem, I used the same approach. Let’s see:

    public static Result updateForm(final Long id) {
    	return new RoleAction(User.class, id, RoleType.ADMIN,RoleType.TENANT)
    			.call(new Function0<Result>() {
    				public Result apply() throws Throwable {
    					User user = JPA.em().find(User.class, id);
    					Form<User> filledForm = userForm.fill(user);
    					return ok(views.html.admin.users.update_form.render(filledForm));
    				}
    			});
    }

It is a little bit verbose, no doubts, but I needed to delivery this feature :). And now I can access all parameters that I need. Just to make the job less annoying, I’ve created a Eclipse template that allows me just write “roleAction” and the code template is generated automatically :).

The good thing about this approach is that, now with Java 8, we can change this implementation in order to use the new lambda support :). Play with Java8 is a lovely thing!

    return new RoleAction(User.class,id,RoleType.ADMIN,RoleType.TENANT)
   .call( () -> {
    	User user = JPA.em().find(User.class, id);
    	Form<User> filledForm = userForm.fill(user);
    	return ok(views.html.admin.users.update_form
    			.render(filledForm));
    });

What do you think? One nice thing here is that I didn’t need to change the original call method. When you have just one method in your interface, the new compiler understands this interface as a Functional Interface and allows you to pass lambda as a parameter. Java 8 support is enabled in Play 2.2.2 and Play 2.3.x!

One model, many validations

It is not unusual a application has more than one html form that should be bound with the same model. Think about this situation: you have a form to register a new user with just a few fields, like name, email and password. After that, you need this same user completes his registration with other fields.

This common situation puts you in a problem. How to partially apply a validation rule in the same model. Below we have the User class:

    package models;

    @Entity
    public class User {

    	private String id;

    	@Required
    	private String name;

    	@Required
    	private String email;

    	@Required
    	private String password;

    	@Valid
    	private Address address = new Address();

    	@Required
    	private String facebookId;

    	//getters and setters
    }

There are a lot of validations! In the first situation, you only need to validate three fields. Let’s see the code:

    public static Result create(){
    	Form<User> filledForm = newUserForm.bindFromRequest("name","email","password");
    	if(filledForm.hasErrors()){
    		return badRequest(views.html.new_user_form.render(filledForm));
    	}
    	User user = filledForm.get();
    	//Ebean.save(user);
    	return redirect(routes.UsersController.form());
    }

Here we allow only the name, email and password are linked to the model. I already commented this in other post. The problem is when we invoke hasErrors method. This method will return true, because our model has other validations. Here we can use a feature provided for Bean Validation that allow us to split validation in different groups.

    package groups;

    public interface ValidationGroups {

    	public interface SocialInformation {}
    	public interface UpdateInformation{}
    	public interface BasicInformation{}

    }

The nice thing about this is that Play’s validation follows Bean Validation spec. We can take advantage of this! We only need to pass the group when we create the Form object.

         private static Form<User> newUserForm = Form.form(User.class,ValidationGroups.BasicInformation.class);

That is it! You can have a lot of groups and use them in specific situations. You still can use multiple groups to perform a validation :).

    @Required(groups={ValidationGroups.BasicInformation.class,ValidationGroups.SocialInformation.class})
    private String email;
    @Required(groups={ValidationGroups.BasicInformation.class,ValidationGroups.SocialInformation.class})
    private String password;
    @Required(groups=ValidationGroups.SocialInformation.class)
    private String facebookId;

And now you pass a different group to form method.

    private static Form<User> newUserForm = Form.form(User.class,ValidationGroups.SocialInformation.class);

Name and email will be validated when a validation is performed with one group or another.

Another alternative, that I try to avoid, is to create a new class that represents your html form. You can use it to bind input values and after that, create your Model. This approach is better when you have a form that is not bound with any model. For example, a form to log in your user. In this case, you can use DynamicForm or a Class that represents login information.

If you have any doubt about PlayFramework, fell free to get in touch. My contact informations is here.