Play User Interface Lesson

I have just completed the UI using Play’s assets. I need to explore the asset controller and compiler. This seems to be interesting and is not part of any Java framework I have used.

 

Image

 

 

I had to install Sass

Play deployment to CloudBees

I followed the instructions to deploy the Play application to CloudBees.

CloudBees SDK

I installed this SDK to interact with the cloud infrastructure. So this created a profile based
on my CloudBees account.

Mohans-MacBook-Pro:cloudbees-sdk-1.5.2 radhakrishnan$ bees help
# CloudBees SDK version: 1.5.2
# CloudBees Driver version: 1.3.8
Installing plugin: org.cloudbees.sdk.plugins:ant-plugin:1.3.0

You have not created a CloudBees configuration profile, let’s create one now…
Enter your default CloudBees API end point [us | eu]: us
Enter your CloudBees account email address: radhakrishnan.mohan@gmail.com
Enter your CloudBees account password:
Plugin installed: org.cloudbees.sdk.plugins:ant-plugin:1.3.0
Installing plugin: org.cloudbees.sdk.plugins:app-plugin:1.5.6
Plugin installed: org.cloudbees.sdk.plugins:app-plugin:1.5.6
Installing plugin: org.cloudbees.sdk.plugins:config-plugin:1.3.2
Plugin installed: org.cloudbees.sdk.plugins:config-plugin:1.3.2
Installing plugin: org.cloudbees.sdk.plugins:db-plugin:1.3.3
Plugin installed: org.cloudbees.sdk.plugins:db-plugin:1.3.3
Installing plugin: com.cloudbees.sdk.plugins:service-plugin:1.2.3
Plugin installed: com.cloudbees.sdk.plugins:service-plugin:1.2.3
Type ‘bees help ‘ for help on a specific subcommand.

SDK subcommands:
help List all commands
init Re-initialize the SDK config file
plugin:delete Delete a SDK plugin
plugin:info Get SDK plugin information

Application deployed to CloudBees

app

Provisioned MySQL on CloudBees

db

The parameters I was supposed to set

Mohans-MacBook-Pro:cloudbees-sdk-1.5.2 radhakrishnan$ bees config:set -a mohanr/playconf AppDynamics=false
Application config parameters for mohanr/playconf: saved

Application Parameters:
applyEvolutions.default=true
DB_USER=mohanr
DB_PASS=test
DB_URL=jdbc:mysql://ec2-50-19-213-178.compute-1.amazonaws.com:3306/playconftest
applyDownEvolutions.default=true
AppDynamics=false
Runtime Parameters:
java_version=1.7

The settings to enable evolutions did not work and I could not access the application.

So I switched off the evolution facility and removed 1.sql and then deployed the application. I think this evolution facility can be switched off in application.conf also.

Application Parameters:
DB_USER=mohanr
DB_PASS=test
applyDownEvolutions.default=false
applyEvolutions.default=false
DB_URL=jdbc:mysql://ec2-23-21-211-172.compute-1.amazonaws.com:3306/helloplaytest
AppDynamics=false
Runtime Parameters:
java_version=1.7

Deployment

Mohans-MacBook-Pro:hello-play radhakrishnan$ ./activator dist
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from /Users/radhakrishnan/Documents/hello-play/project
[warn] Multiple resolvers having different access mechanism configured with same name ‘typesafe-ivy-releases’. To avoid conflict, Remove duplicate project resolvers (`resolvers`) or rename publishing resolver (`publishTo`).
[info] Set current project to hello-play (in build file:/Users/radhakrishnan/Documents/hello-play/)
[info] Packaging /Users/radhakrishnan/Documents/hello-play/target/scala-2.10/hello-play_2.10-1.0-SNAPSHOT-sources.jar …
[info] Done packaging.
[info] Main Scala API documentation to /Users/radhakrishnan/Documents/hello-play/target/scala-2.10/api…
[info] Wrote /Users/radhakrishnan/Documents/hello-play/target/scala-2.10/hello-play_2.10-1.0-SNAPSHOT.pom
[info] Packaging /Users/radhakrishnan/Documents/hello-play/target/scala-2.10/hello-play_2.10-1.0-SNAPSHOT.jar …
[info] Done packaging.
model contains 30 documentable templates
[info] Main Scala API documentation successful.
[info] Packaging /Users/radhakrishnan/Documents/hello-play/target/scala-2.10/hello-play_2.10-1.0-SNAPSHOT-javadoc.jar …
[info] Done packaging.
[info]
[info] Your package is ready in /Users/radhakrishnan/Documents/hello-play/target/universal/hello-play-1.0-SNAPSHOT.zip
[info]
[success] Total time: 16 s, completed Feb 23, 2014 9:23:11 PM
Mohans-MacBook-Pro:hello-play radhakrishnan$ bees app:deploy -a playconf -t play2 target/universal/hello-play-1.0-SNAPSHOT.zip
-bash: bees: command not found
Mohans-MacBook-Pro:hello-play radhakrishnan$ source ~/.profile
Mohans-MacBook-Pro:hello-play radhakrishnan$ bees app:deploy -a playconf -t play2 target/universal/hello-play-1.0-SNAPSHOT.zip
Deploying application mohanr/playconf (environment: ): target/universal/hello-play-1.0-SNAPSHOT.zip
Application parameters: {containerType=play2}
……………………uploaded 25%
……………………uploaded 50%
……………………uploaded 75%
……………………upload completed
deploying application to server(s)…
Application mohanr/playconf deployed: http://playconf.mohanr.cloudbees.net

Create tables using MySQL Workbench

MySQL Workbench

The application was accessible now as the following screenshots show.

New Proposal

Submitted Proposal

Play framework form validation errors

I post as I work my way through the Play material. I have not still read The reactive manifesto and relate it to Play. So I used the same classic trick popularized by Struts to debug this. As far as this type of error display mechanism is concerned I did not find anything different between Play and old versions of Struts.

Problem

I did not initialize isApproved to false.

package models;

import javax.persistence.*;
import javax.validation.Valid;

import play.data.validation.Constraints.MaxLength;
import play.data.validation.Constraints.MinLength;
import play.data.validation.Constraints.Required;
import play.db.ebean.*;

@Entity public class Proposal extends Model {

	@Id
	public String id;
	
	@Required public String title;
	
	@MinLength(value = 10)
	@MaxLength(value = 1000)
	@Column(length=1000)
	public String proposal;
	
	@Required public SessionType type = SessionType.OneHourTalk;
	
	@Required public Boolean isApproved;
	
	public String keywords;
	
	@Valid
	@OneToOne(cascade=CascadeType.ALL)
	public Speaker speaker;
}

Display errors

@(proposal: play.data.Form[Proposal])

@import helper._


@main("New Proposal"){

	@if(proposal.hasErrors) {
        <div class="alert alert-error">
            @proposal.errors
        </div>
	}

	@form(action = routes.MainController.submitProposal()){
	<h3>Proposal</h3>
    @inputText(proposal("title"))
    @textarea(proposal("proposal"))
    @inputText(proposal("keywords"))
	<h3>Speaker</h3>
    @inputText(proposal("speaker.name"))
    @inputText(proposal("speaker.email"))
    @textarea(proposal("speaker.bio"))
    @inputText(proposal("speaker.twitterId"))
    @inputText(proposal("speaker.pictureUrl"))
    
	<p class="submit">
				<button type="submit" class="button green" id="submitForm">Submit</button>
	</p>
    }
}

Error message

I can customize it but this sufficed.

Screen Shot 2014-02-23 at 5.14.17 PM

Default settings for Play Java applications

I started worked with a play example and encountered this error.


package controllers;

 

import models.Proposal;

 

import play.data.Form;

import play.mvc.Controller;

import play.mvc.Result;

import play.mvc.Results;

import models.*;

 

public class MainController extends Controller {

  

    public static Result welcome(String name) {

        return ok("<h1> Welcome " + name + "</h1>").as("text/html");

    }

 

    public static Result index() {

        return ok(views.html.index.render("Hi from Java"));

    }

    

    public static Result newProposal(){

    Form<Proposal> proposalForm = Form.form(Proposal.class);

    

    return ok(views.html.newProposal.render(proposalForm));

    }

    

    public static Result submitProposal(){

    return Results.TODO;

    }

    

}

Error

[error] /Users/radhakrishnan/Documents/hello-play/app/controllers/MainController.java:23: error: incompatible types: play.data.Form cannot be converted to play.api.data.Form
[error] return ok(views.html.newProposal.render(proposalForm));

I struggled with this error until I set play.Project.playJavaSettings in my build.sbt

Play Framework

I was trying to use the activator UI to play with the Play Framework. It worked except for this annoying issue.

So I switched to the eclipse setup which works quite well.

[hello-play] $ Mohans-MacBook-Pro:hello-play radhakrishnan$ ./activator
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from /Users/radhakrishnan/Documents/hello-play/project
[warn] Multiple resolvers having different access mechanism configured with same name ‘typesafe-ivy-releases’. To avoid conflict, Remove duplicate project resolvers (`resolvers`) or rename publishing resolver (`publishTo`).
[info] Set current project to hello-play (in build file:/Users/radhakrishnan/Documents/hello-play/)

Generate an eclipse project

[hello-play] $ eclipse with-source=true
[info] About to create Eclipse project files for your project(s).
[info] Resolving com.typesafe.atmos#atmos-sigar-libs;1.3.1 …
[info] downloading http://repo1.maven.org/maven2/org/scala-lang/scala-library/2.10.2/scala-library-2.10.2-sources.jar
[info] [SUCCESSFUL ] org.scala-lang#scala-library;2.10.2!scala-library.jar(src) (41827ms)

Change and save

Screen Shot 2014-02-16 at 8.57.20 PM

Hot deploy

[hello-play] $ ~run

— (Running the application from SBT, auto-reloading is enabled) —

[info] play – Listening for HTTP on /0.0.0.0:9000

(Server started, use Ctrl+D to stop and go back to the console…)

[success] Compiled in 715ms
[info] play – Application started (Dev)
[info] Compiling 1 Java source to /Users/radhakrishnan/Documents/hello-play/target/scala-2.10/classes…
[success] Compiled in 4s

— (RELOAD) —

[info] play – Application started (Dev)

Change in view

Screen Shot 2014-02-16 at 8.53.10 PM

NIO.2

This post is not exactly about NIO.2 even though I use SeekableByteChannel. It is about a pestering question asked by two interviewers in the past. I believe a coding task is a good and reliable first step in any interview process. But some people ask questions in a patronising way.

This is one such question. How do you identify duplicate rows in a file ?

So I decided to do what any programmer worth her salt will do. This is not the most efficient way but I wanted to try NIO.2’s SeekableByteChannel because none of the firms I have worked for in the past has any need for any new Java API. They still wallow in legacy Java applications. I don’t get a chance to use it for any project.

Sample file to parse

I wanted to filter duplicates in column 3 – the name of the page requested – from a sample log.


timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,Latency
1346999466187,32,Home page - anon,200,OK,Anonymous Browsing 1-2,text,true,31
1346999466182,37,Login form,200,OK,Node save 3-1,text,true,36
1346999466184,35,Home page - anon,200,OK,Anonymous Browsing 1-11,text,true,32
1346999466182,37,Home page - anon,200,OK,Anonymous Browsing 1-1,text,true,34
1346999466189,30,Home page - anon,200,OK,Anonymous Browsing 1-4,text,true,27
1346999466185,46,Home page - anon,200,OK,Anonymous Browsing 1-5,text,true,34
1346999466185,44,Search,200,OK,Search 4-1,text,true,35
1346999466188,28,Home page - anon,200,OK,Anonymous Browsing 1-3,text,true,26
1346999466182,33,Home page - anon,200,OK,Anonymous Browsing 1-7,text,true,32
1346999466182,36,Login Form,200,OK,Perform Login/View Account 5-1,text,true,35
1346999466182,35,Home page - anon,200,OK,Anonymous Browsing 1-10,text,true,33

Sample Java code that is not efficient


package com.test;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;
import java.util.Set;
import java.util.TreeSet;

public class DuplicateTest {

    Set<String> values = new TreeSet<>();

    public static void main( String... argv ){

        DuplicateTest dt = new DuplicateTest();

        dt.analyze();
    }

    private  void analyze() {
        
        Path p = Paths.get(File.separator + "Users"
                                    + File.separator + "radhakrishnan" +
                                            File.separator + "Lambdas", "duplicate.txt");
        ByteBuffer b = ByteBuffer.allocate(1);
        
        String encoding = System.getProperty( "file.encoding");
        
        b.clear();
        
        char c;
        
        StringBuilder sb = new StringBuilder();
        
        try(
            SeekableByteChannel skbc = Files.newByteChannel(p,
                                                            EnumSet.of(StandardOpenOption.READ))) {
            //Move the position after the first line(heading)
            skbc.position(89);

            while( skbc.read( b ) > 0){
                
                   b.flip();
                
                   c = Charset.forName(encoding).decode(b).get();
                
                   //System.out.println(c);
                   sb.append(c);
                
                   if( c == '\n' || c == '\r'){
                       //Move the position to read the line after the new line
                       skbc.position(skbc.position());
                       extractAndStore(sb.toString());
                       sb = new StringBuilder();
                   }
                   b.clear();
            }

        } catch (IOException e) {
            
            e.printStackTrace();
            
        }
        
        System.out.println(values);
    }

    private  void extractAndStore(String s) {

        values.add(s.split("[,]")[2]);
    }

}

Result

[Home page - anon, Home page - auth, Login, Login Form, Login form, Logout, Node edit form, Random node - anon, Search, User profile page, node edit post]