SlideShare a Scribd company logo
Having Fun with


or, how a perfectly normal
dynamic language developer
fell in love with Java

                   Clinton R. Nixon, Viget Labs
i am a robot come
About Me    from the year 2082
           in order to prevent
            java from becoming
              stale and borinG
“We  are  fully  aware  that  we  made  choices  that  are  pretty  
uncommon  in  the  Java  world,  and  that  Play  does  not  
blindly  follow  all  the  so-­‐called  Java  ‘good  practices’.  But  
all  members  of  the  Play  teams  are  very  experienced  Java  
developers  and  we  are  totally  aware  of  the  choices  we  
made  and  the  rules  we  broke.

“Java  itself  is  a  very  generic  programming  language  and  
not  originally  designed  for  web  application  development.  
It  is  a  very  different  thing  to  write  a  generic  and  reusable  
Java  library  and  to  create  a  web  application.  A  web  
application  itself  doesn’t  need  to  be  designed  to  be  
reusable.  You  need  less  abstraction,  less  conEiguration.  
Reusability  does  exist  for  Web  applications,  but  through  
Web  service  API  rather  than  language-­‐level  integration.”

      –  Guillaume  Bort,  from  the  Play!  FAQ  (emphasis  mine)
What is Play!?
(a) A  web  framework  with  unnecessary  punctation  
    in  its  name  that  I  am  now  dropping.
(b) A  web  framework  written  in  Java  that  doesn’t  
    use  servlets,  portlets,  XML,  EJBs,  JSPs,  or  PCP.
(c) A  share-­‐nothing,  REST-­‐aware,  super-­‐fast,  MVC-­‐
    structured  web  framework  that  takes  the  best  
    from  Ruby  on  Rails,  Django,  and  the  like  and  
    adds  its  own  strengths.
(d) All  of  the  above.
Our example app: PDFless

A  way  to  build  PDFs  
using  simple  tools:  
HTML  +  CSS.

Good  candidate  for  a  
system  like  Play.  Uses  
FlyingSaucer,  a  Java  
library.
Starting our app
cnixon@moro  ~/Projects>  play  new  pdfless                                                                              
                                                                                                                           
~                _                        _  
~    _  __  |  |  __  _  _    _|  |                                      Look at tha t ASCII art!
~  |  '_  |  |/  _'  |  ||  |_|
~  |    __/|_|____|__  (_)                                             Now we’re  having fun.
~  |_|                        |__/      
~
~  play!  1.1-­‐unstable-­‐r785,  http://www.playframework.org
~
~  The  new  application  will  be  created  in  /Users/cnixon/Projects/pdfless
~  What  is  the  application  name?  PDFless
~
~  OK,  the  application  is  created.
~  Start  it  with  :  play  run  pdfless
~  Have  fun!
Our model
Creating our model
package  models;
import  play.data.validation.Email;
                                              app/models/Document.java
import  play.data.validation.Required;

                                                             public fields
import  play.data.validation.URL;
import  play.db.jpa.Model;
import  javax.persistence.Entity;


@Entity
public  class  Document  extends  Model  {
    @Required  @URL
    public  String  url;

    @Required  @Email
    public  String  email;

    @Required
    public  boolean  processed;                        annotations for validation
    public  Document(String  url,  String  email)  {
        this.url  =  url;
        this.email  =  email;
        this.processed  =  false;
    }
}
Where’s the database?
    #  Database  configuration
#  ~~~~~  
#  To  quickly  set  up  a  development  database,  use  either:
#      -­‐  mem  :  for  a  transient  in  memory  database  (HSQL  in  memory)
#      -­‐  fs    :  for  a  simple  file  written  database  (HSQL  file  stored)
db=fs
#                                for testing, I’m using an in-memory DB
#  To  connect  to  a  local  MySQL5  database,  use:
#  db=mysql:user:pwd@database_name
#
#  If  you  need  a  full  JDBC  configuration  use  the  following  :
#  db.url=jdbc:postgresql:database_name
#  db.driver=org.postgresql.Driver
#  db.user=root
#  db.pass=secret
                                              There’s no migration system:
                                  Hibernate will auto-update your
                         tables unless you configure it otherwise.
Document.findById(id);
Document.find("processed  =  ?",  true);
Document.findAll();
Document.count();
Document.delete("email  =  ?",  "crnixon@gmail.com");

Document  document  =  new  Document("http://google.com",  
                                                                  "crnixon@gmail.com");
document.save();
document.delete();
How could this work?
      public  class  JPAEnhancer  extends  Enhancer  {
          public  void  enhanceThisClass(ApplicationClass  applicationClass)  throws  
      Exception  {
              CtClass  ctClass  =  makeClass(applicationClass);
        String  entityName  =  ctClass.getName();          from Javassist,
        //  ...
                                       a Java bytecode manipulation library
        //  count
        CtMethod  count  =  CtMethod.make("public  static  long  count()  {  return  
play.db.jpa.JPQL.instance.count(""  +  entityName  +  "");  }",  ctClass);
        ctClass.addMethod(count);

        //  find                
        CtMethod  find  =  CtMethod.make("public  static  play.db.jpa.JPASupport.JPAQuery  
find(String  query,  Object[]  params)  {  return  play.db.jpa.JPQL.instance.find(""  +  
entityName  +  "",  query,  params);  }",  ctClass);
        ctClass.addMethod(find);
    }
}                                          this looks like some sort
                                     of Ruby metaprogramming nonsense
Our controller
A simple controller
   package  controllers;

import  models.Document;
                                      What does this remind me of?
import  play.data.validation.Valid;
import  play.mvc.Controller;

public  class  Documents  extends  Controller  {
    public  static  void  index()  {
        render();        
    }

    public  static  void  create(@Valid  Document  doc)  {
        if  (validation.hasErrors())  {
            render("Documents/index.html");
        }  else  {
            doc.save();
        }
    }
}
Ok, this smells like a train yard

    #  Routes
#  This  file  defines  all  application  routes  (Higher  priority  routes  first)
#  ~~~~
                                                       this took some getting used to
#  Home  page
GET          /                                                                              Documents.index
GET          /documents                                                            Documents.index
POST        /documents                                                            Documents.create

#  Map  static  resources  from  the  /app/public  folder  to  their  paths
GET          /public/                                                                staticDir:public

#  Catch  all
*              /{controller}/{action}                                    {controller}.{action}



                         sigh - but it is helpful with rapid development
Live demonstration
In case you didn’t catch that

• No  compiling
• Awesome  error  pages
• Request  params  bind  to  
 controller  method  
 params
Let’s see that redirection code
                                                       This is hard to understand,
   if  (Modifier.isPublic(ctMethod.getModifiers())  &&  
               ((ctClass.getName().endsWith("$")  &&  
                                                                   but look at this!
                   !ctMethod.getName().contains("$default$"))  ||  
                   (Modifier.isStatic(ctMethod.getModifiers())  &&  
                   ctMethod.getReturnType().equals(CtClass.voidType)))  &&  
                   !isHandler)  {
           try  {
               ctMethod.insertBefore(
                   "if(!
       play.classloading.enhancers.ControllersEnhancer.ControllerInstrumentation.isActionCallAllowed
       ())  {"+
                   "play.mvc.Controller.redirect(""+ctClass.getName().replace("$",  "")+
            "."+ctMethod.getName()+"",  $args);"+
            "}"+
            "play.classloading.enhancers.ControllersEnhancer.ControllerInstrumentation.stopActionCall();"
            );
    }  catch  (Exception  e)  {  ...  }
}
Translation to human
Before each method, add      “If we’re already in an action call
  ctMethod.insertBefore(
              "if(!play.classloading.enhancers.
  ControllersEnhancer.ControllerInstrumentation.
  isActionCallAllowed())  {"+
              "play.mvc.Controller.redirect(""+    send a redirect
              ctClass.getName().replace("$",  "")+      to this new
              "."+ctMethod.getName()+"",  $args);"+         action
              "}"+                                  to the browser
              "play.classloading.enhancers.
  ControllersEnhancer.ControllerInstrumentation.
  stopActionCall();"
  );
                                and stop the original action.”
Our view
#{extends  'main.html'  /}                               this is Groovy
#{set  title:'PDFLess'  /}

#{ifErrors}
<div  class="error">Please  correct  the  errors  below.</div>
#{/ifErrors}

#{form  @Documents.create()}
    <p>
        <label  for="doc.url">Enter  the  URL  to  make  a  PDF  out  of:</label>
        <input  type="text"  id="doc.url"  name="doc.url"  value="${params['doc.url']}"/>
        #{ifError  'doc.url'}
            <span  class="error">#{error  'doc.url'  /}</span>
        #{/ifError}
    </p>                                                         this is gross
    <p>
        <label  for="doc.email">Enter  the  email  address  to  send  your  PDF  to:</label>
        <input  type="text"  id="doc.email"  name="doc.email"  
            value="${params['doc.email']}"/>
        #{ifError  'doc.email'}
            <span  class="error">#{error  'doc.email'  /}</span>
        #{/ifError}
    </p>
    <input  type="submit"  id="doc.submit"  />
#{/form}
                                                           but this is kind of
                                                               awesome
tags are just
app/views/tags/textField.html
<p>                                                           HTML + Groovy
    <label  for="${_arg}">#{doBody  /}</label>
    <input  type="text"  id="${_arg}"  name="${_arg}"  value="${params[_arg]}"/>
    #{ifError  _arg}
        <span  class="error">#{error  _arg  /}</span>
    #{/ifError}
</p>                                                        and are used like
app/views/Documents/index.html                              built-in tags
#{form  @Documents.create()}
    #{textField  'doc.url'}Enter  the  URL  to  make  a  PDF  out  of:#{/textField}
    #{textField  'doc.email'}
        Enter  the  email  address  to  send  your  PDF  to:
    #{/textField}
    #{textField  'code'}Please  type  the  code  below:#{/textField}    
    <p>
        <input  type="hidden"  name="randomID"  value="${randomID}"  />
        <img  class="captcha"  src="@{Application.captcha(randomID)}"  />
    </p>
    <input  type="submit"  id="doc.submit"  value="Make  My  PDF"  />
#{/form}
Processing jobs
Jobs

Jobs  let  you  perform  
actions  outside  of  web  
requests.
They  take  the  place  of  
startup  tasks,  cron  jobs,  
and  can  be  used  instead  
of  queues.
Startup jobs
  package  jobs;
import  play.jobs.Job;
import  play.jobs.OnApplicationStart;

@OnApplicationStart
public  class  SeedDataJob  extends  Job  {
    public  void  doJob()  {
        loadSeedData("db/seed.yml");
    }
}
Periodic jobs
  package  jobs;
import  play.jobs.Job;
import  play.jobs.Every;

@Every("2h")
public  class  TempDirCleanJob  extends  Job  {
    public  void  doJob()  {
        cleanUpOldPDFs();
    }
}
Using modules
Current modules
• CRUD                   • Database  migrations
• Authentication  &      • Sass
 authorization
                         • Routes  via  annotations
• Captcha  generation    • Scala  &  Scalate
• Code  coverage         • Search
• Google  App  Engine    • Guice
• Google  Web  Toolkit   • Spring
• Oauth                  • and  more!
Testing this thing
Integrated testing tools


JUnit,  Selenium
You  cannot  be  told  what  the  JUnit  is.
Community
This guy is a moron




This guy is really nice
Young, but shows promise
             The  mailing  list  for  Play  is  
             very  active,  and  new  
             modules  are  being  created  
             all  the  time.

             Reminds  me  of  Rails  
             around  1.0.

             Could  easily  blow  up;  
             could  easily  slowly  die  off.
Awesome things we don’t have time for

• Suspendable  requests
• Easiest  Eile  uploads  ever
• Bespin  module  to  code  your  application  inside  your  
  application
• Automatic  type  extensions  in  templates
• Simple  i18n
Summary
Play  comes  to  Java  with  as  few  preconceptions  as  
possible.
It  hooks  into  existing  Java  libraries  easily.
It’s  surprisingly  fun  to  use  and  quick  to  develop  with.
It’s  a  project  with  lots  of  room  for  individuals  to  
make  a  difference.

If  you  liked  this  talk,  please   http://playframework.org    
consider  donating  to  the  
Buy  Clinton  IntelliJ  fund.  
                                              http://crnixon.org
My  free  tial  ends  in  3  days.                     @crnixon

More Related Content

What's hot

Geotalk presentation
Geotalk presentationGeotalk presentation
Geotalk presentation
Eric Palakovich Carr
 
Testing persistence in PHP with DbUnit
Testing persistence in PHP with DbUnitTesting persistence in PHP with DbUnit
Testing persistence in PHP with DbUnitPeter Wilcsinszky
 
Spring data iii
Spring data iiiSpring data iii
Spring data iii
명철 강
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
Daniel Cukier
 
Devoxx 15 equals hashcode
Devoxx 15 equals hashcodeDevoxx 15 equals hashcode
Devoxx 15 equals hashcodebleporini
 
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
Srijan Technologies
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutions
Solution4Future
 
KISS Automation.py
KISS Automation.pyKISS Automation.py
KISS Automation.py
Iakiv Kramarenko
 
Spring 4 - A&BP CC
Spring 4 - A&BP CCSpring 4 - A&BP CC
Spring 4 - A&BP CC
JWORKS powered by Ordina
 
"Migrate large gwt applications - Lessons Learned" By Harald Pehl
"Migrate large gwt applications - Lessons Learned" By Harald Pehl"Migrate large gwt applications - Lessons Learned" By Harald Pehl
"Migrate large gwt applications - Lessons Learned" By Harald Pehl
GWTcon
 
Aligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsAligning Ember.js with Web Standards
Aligning Ember.js with Web Standards
Matthew Beale
 
Maintainable JavaScript 2012
Maintainable JavaScript 2012Maintainable JavaScript 2012
Maintainable JavaScript 2012
Nicholas Zakas
 
RSpec 3.0: Under the Covers
RSpec 3.0: Under the CoversRSpec 3.0: Under the Covers
RSpec 3.0: Under the Covers
Brian Gesiak
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
Jeff Durta
 
Using Task Queues and D3.js to build an analytics product on App Engine
Using Task Queues and D3.js to build an analytics product on App EngineUsing Task Queues and D3.js to build an analytics product on App Engine
Using Task Queues and D3.js to build an analytics product on App Engine
River of Talent
 
Redis for your boss 2.0
Redis for your boss 2.0Redis for your boss 2.0
Redis for your boss 2.0
Elena Kolevska
 
Angular beans
Angular beansAngular beans
Angular beans
Bessem Hmidi
 
Redis for your boss
Redis for your bossRedis for your boss
Redis for your boss
Elena Kolevska
 
Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3
makoto tsuyuki
 

What's hot (20)

Geotalk presentation
Geotalk presentationGeotalk presentation
Geotalk presentation
 
Testing persistence in PHP with DbUnit
Testing persistence in PHP with DbUnitTesting persistence in PHP with DbUnit
Testing persistence in PHP with DbUnit
 
Spring data iii
Spring data iiiSpring data iii
Spring data iii
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
 
Devoxx 15 equals hashcode
Devoxx 15 equals hashcodeDevoxx 15 equals hashcode
Devoxx 15 equals hashcode
 
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutions
 
KISS Automation.py
KISS Automation.pyKISS Automation.py
KISS Automation.py
 
Spring 4 - A&BP CC
Spring 4 - A&BP CCSpring 4 - A&BP CC
Spring 4 - A&BP CC
 
"Migrate large gwt applications - Lessons Learned" By Harald Pehl
"Migrate large gwt applications - Lessons Learned" By Harald Pehl"Migrate large gwt applications - Lessons Learned" By Harald Pehl
"Migrate large gwt applications - Lessons Learned" By Harald Pehl
 
Aligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsAligning Ember.js with Web Standards
Aligning Ember.js with Web Standards
 
Maintainable JavaScript 2012
Maintainable JavaScript 2012Maintainable JavaScript 2012
Maintainable JavaScript 2012
 
RSpec 3.0: Under the Covers
RSpec 3.0: Under the CoversRSpec 3.0: Under the Covers
RSpec 3.0: Under the Covers
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
Using Task Queues and D3.js to build an analytics product on App Engine
Using Task Queues and D3.js to build an analytics product on App EngineUsing Task Queues and D3.js to build an analytics product on App Engine
Using Task Queues and D3.js to build an analytics product on App Engine
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
Redis for your boss 2.0
Redis for your boss 2.0Redis for your boss 2.0
Redis for your boss 2.0
 
Angular beans
Angular beansAngular beans
Angular beans
 
Redis for your boss
Redis for your bossRedis for your boss
Redis for your boss
 
Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3
 

Viewers also liked

Narwhal and the Adventures of CommonJS
Narwhal and the Adventures of CommonJSNarwhal and the Adventures of CommonJS
Narwhal and the Adventures of CommonJSClinton Dreisbach
 
HTML5 Now
HTML5 NowHTML5 Now
Unearthed Arcana for Web People
Unearthed Arcana for Web PeopleUnearthed Arcana for Web People
Unearthed Arcana for Web PeopleClinton Dreisbach
 
Ruby On Rails coding conventions, standards and best practices
Ruby On Rails coding conventions, standards and best practicesRuby On Rails coding conventions, standards and best practices
Ruby On Rails coding conventions, standards and best practicesDavid Paluy
 
Ruby on Rails - The Best Track for your Start Up
Ruby on Rails - The Best Track for your Start UpRuby on Rails - The Best Track for your Start Up
Ruby on Rails - The Best Track for your Start Up
Prateek Saxena
 
Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3Clinton Dreisbach
 

Viewers also liked (7)

Narwhal and the Adventures of CommonJS
Narwhal and the Adventures of CommonJSNarwhal and the Adventures of CommonJS
Narwhal and the Adventures of CommonJS
 
The Joy Of Ruby
The Joy Of RubyThe Joy Of Ruby
The Joy Of Ruby
 
HTML5 Now
HTML5 NowHTML5 Now
HTML5 Now
 
Unearthed Arcana for Web People
Unearthed Arcana for Web PeopleUnearthed Arcana for Web People
Unearthed Arcana for Web People
 
Ruby On Rails coding conventions, standards and best practices
Ruby On Rails coding conventions, standards and best practicesRuby On Rails coding conventions, standards and best practices
Ruby On Rails coding conventions, standards and best practices
 
Ruby on Rails - The Best Track for your Start Up
Ruby on Rails - The Best Track for your Start UpRuby on Rails - The Best Track for your Start Up
Ruby on Rails - The Best Track for your Start Up
 
Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3
 

Similar to Having Fun with Play

[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
Alive Kuo
 
Zend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_Tool
Gordon Forsythe
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
toddbr
 
HTML5 for the Silverlight Guy
HTML5 for the Silverlight GuyHTML5 for the Silverlight Guy
HTML5 for the Silverlight Guy
David Padbury
 
Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.js
davidchubbs
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
Alexander Varwijk
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
dion
 
Nodejs.meetup
Nodejs.meetupNodejs.meetup
Nodejs.meetup
Vivian S. Zhang
 
I Feel Pretty
I Feel PrettyI Feel Pretty
I Feel Pretty
John Quaglia
 
Boston Computing Review - Java Server Pages
Boston Computing Review - Java Server PagesBoston Computing Review - Java Server Pages
Boston Computing Review - Java Server Pages
John Brunswick
 
Everything is Awesome - Cutting the Corners off the Web
Everything is Awesome - Cutting the Corners off the WebEverything is Awesome - Cutting the Corners off the Web
Everything is Awesome - Cutting the Corners off the Web
James Rakich
 
Sprockets
SprocketsSprockets
Demystifying Maven
Demystifying MavenDemystifying Maven
Demystifying Maven
Mike Desjardins
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
ciklum_ods
 
Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018
Ortus Solutions, Corp
 
Into The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and dockerInto The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and docker
Ortus Solutions, Corp
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
Bubbles & Trees with jQuery
Bubbles & Trees with jQueryBubbles & Trees with jQuery
Bubbles & Trees with jQuery
Bastian Feder
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 

Similar to Having Fun with Play (20)

[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
Zend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_Tool
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
HTML5 for the Silverlight Guy
HTML5 for the Silverlight GuyHTML5 for the Silverlight Guy
HTML5 for the Silverlight Guy
 
Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.js
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
 
Nodejs.meetup
Nodejs.meetupNodejs.meetup
Nodejs.meetup
 
I Feel Pretty
I Feel PrettyI Feel Pretty
I Feel Pretty
 
Boston Computing Review - Java Server Pages
Boston Computing Review - Java Server PagesBoston Computing Review - Java Server Pages
Boston Computing Review - Java Server Pages
 
Everything is Awesome - Cutting the Corners off the Web
Everything is Awesome - Cutting the Corners off the WebEverything is Awesome - Cutting the Corners off the Web
Everything is Awesome - Cutting the Corners off the Web
 
Sprockets
SprocketsSprockets
Sprockets
 
Demystifying Maven
Demystifying MavenDemystifying Maven
Demystifying Maven
 
UNO based ODF Toolkit API
UNO based ODF Toolkit APIUNO based ODF Toolkit API
UNO based ODF Toolkit API
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018Going live with BommandBox and docker Into The Box 2018
Going live with BommandBox and docker Into The Box 2018
 
Into The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and dockerInto The Box 2018 Going live with commandbox and docker
Into The Box 2018 Going live with commandbox and docker
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
Bubbles & Trees with jQuery
Bubbles & Trees with jQueryBubbles & Trees with jQuery
Bubbles & Trees with jQuery
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 

Recently uploaded

Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
Product School
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
Paul Groth
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
Ralf Eggert
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
Product School
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 

Recently uploaded (20)

Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)PHP Frameworks: I want to break free (IPC Berlin 2024)
PHP Frameworks: I want to break free (IPC Berlin 2024)
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 

Having Fun with Play

  • 1. Having Fun with or, how a perfectly normal dynamic language developer fell in love with Java Clinton R. Nixon, Viget Labs
  • 2. i am a robot come About Me from the year 2082 in order to prevent java from becoming stale and borinG
  • 3. “We  are  fully  aware  that  we  made  choices  that  are  pretty   uncommon  in  the  Java  world,  and  that  Play  does  not   blindly  follow  all  the  so-­‐called  Java  ‘good  practices’.  But   all  members  of  the  Play  teams  are  very  experienced  Java   developers  and  we  are  totally  aware  of  the  choices  we   made  and  the  rules  we  broke. “Java  itself  is  a  very  generic  programming  language  and   not  originally  designed  for  web  application  development.   It  is  a  very  different  thing  to  write  a  generic  and  reusable   Java  library  and  to  create  a  web  application.  A  web   application  itself  doesn’t  need  to  be  designed  to  be   reusable.  You  need  less  abstraction,  less  conEiguration.   Reusability  does  exist  for  Web  applications,  but  through   Web  service  API  rather  than  language-­‐level  integration.” –  Guillaume  Bort,  from  the  Play!  FAQ  (emphasis  mine)
  • 4. What is Play!? (a) A  web  framework  with  unnecessary  punctation   in  its  name  that  I  am  now  dropping. (b) A  web  framework  written  in  Java  that  doesn’t   use  servlets,  portlets,  XML,  EJBs,  JSPs,  or  PCP. (c) A  share-­‐nothing,  REST-­‐aware,  super-­‐fast,  MVC-­‐ structured  web  framework  that  takes  the  best   from  Ruby  on  Rails,  Django,  and  the  like  and   adds  its  own  strengths. (d) All  of  the  above.
  • 5. Our example app: PDFless A  way  to  build  PDFs   using  simple  tools:   HTML  +  CSS. Good  candidate  for  a   system  like  Play.  Uses   FlyingSaucer,  a  Java   library.
  • 6. Starting our app cnixon@moro  ~/Projects>  play  new  pdfless                                                                                 ~                _                        _   ~    _  __  |  |  __  _  _    _|  | Look at tha t ASCII art! ~  |  '_  |  |/  _'  |  ||  |_| ~  |    __/|_|____|__  (_) Now we’re having fun. ~  |_|                        |__/       ~ ~  play!  1.1-­‐unstable-­‐r785,  http://www.playframework.org ~ ~  The  new  application  will  be  created  in  /Users/cnixon/Projects/pdfless ~  What  is  the  application  name?  PDFless ~ ~  OK,  the  application  is  created. ~  Start  it  with  :  play  run  pdfless ~  Have  fun!
  • 8. Creating our model package  models; import  play.data.validation.Email; app/models/Document.java import  play.data.validation.Required; public fields import  play.data.validation.URL; import  play.db.jpa.Model; import  javax.persistence.Entity; @Entity public  class  Document  extends  Model  {    @Required  @URL    public  String  url;    @Required  @Email    public  String  email;    @Required    public  boolean  processed; annotations for validation    public  Document(String  url,  String  email)  {        this.url  =  url;        this.email  =  email;        this.processed  =  false;    } }
  • 9. Where’s the database? #  Database  configuration #  ~~~~~   #  To  quickly  set  up  a  development  database,  use  either: #      -­‐  mem  :  for  a  transient  in  memory  database  (HSQL  in  memory) #      -­‐  fs    :  for  a  simple  file  written  database  (HSQL  file  stored) db=fs # for testing, I’m using an in-memory DB #  To  connect  to  a  local  MySQL5  database,  use: #  db=mysql:user:pwd@database_name # #  If  you  need  a  full  JDBC  configuration  use  the  following  : #  db.url=jdbc:postgresql:database_name #  db.driver=org.postgresql.Driver #  db.user=root #  db.pass=secret There’s no migration system: Hibernate will auto-update your tables unless you configure it otherwise.
  • 10. Document.findById(id); Document.find("processed  =  ?",  true); Document.findAll(); Document.count(); Document.delete("email  =  ?",  "crnixon@gmail.com"); Document  document  =  new  Document("http://google.com",                                                                    "crnixon@gmail.com"); document.save(); document.delete();
  • 11. How could this work? public  class  JPAEnhancer  extends  Enhancer  {    public  void  enhanceThisClass(ApplicationClass  applicationClass)  throws   Exception  {        CtClass  ctClass  =  makeClass(applicationClass);        String  entityName  =  ctClass.getName(); from Javassist,        //  ... a Java bytecode manipulation library        //  count        CtMethod  count  =  CtMethod.make("public  static  long  count()  {  return   play.db.jpa.JPQL.instance.count(""  +  entityName  +  "");  }",  ctClass);        ctClass.addMethod(count);        //  find                        CtMethod  find  =  CtMethod.make("public  static  play.db.jpa.JPASupport.JPAQuery   find(String  query,  Object[]  params)  {  return  play.db.jpa.JPQL.instance.find(""  +   entityName  +  "",  query,  params);  }",  ctClass);        ctClass.addMethod(find);    } } this looks like some sort of Ruby metaprogramming nonsense
  • 13. A simple controller package  controllers; import  models.Document; What does this remind me of? import  play.data.validation.Valid; import  play.mvc.Controller; public  class  Documents  extends  Controller  {    public  static  void  index()  {        render();            }    public  static  void  create(@Valid  Document  doc)  {        if  (validation.hasErrors())  {            render("Documents/index.html");        }  else  {            doc.save();        }    } }
  • 14. Ok, this smells like a train yard #  Routes #  This  file  defines  all  application  routes  (Higher  priority  routes  first) #  ~~~~ this took some getting used to #  Home  page GET          /                                                                              Documents.index GET          /documents                                                            Documents.index POST        /documents                                                            Documents.create #  Map  static  resources  from  the  /app/public  folder  to  their  paths GET          /public/                                                                staticDir:public #  Catch  all *              /{controller}/{action}                                    {controller}.{action} sigh - but it is helpful with rapid development
  • 16. In case you didn’t catch that • No  compiling • Awesome  error  pages • Request  params  bind  to   controller  method   params
  • 17. Let’s see that redirection code This is hard to understand, if  (Modifier.isPublic(ctMethod.getModifiers())  &&              ((ctClass.getName().endsWith("$")  &&   but look at this!            !ctMethod.getName().contains("$default$"))  ||              (Modifier.isStatic(ctMethod.getModifiers())  &&              ctMethod.getReturnType().equals(CtClass.voidType)))  &&              !isHandler)  {    try  {        ctMethod.insertBefore(            "if(! play.classloading.enhancers.ControllersEnhancer.ControllerInstrumentation.isActionCallAllowed ())  {"+            "play.mvc.Controller.redirect(""+ctClass.getName().replace("$",  "")+            "."+ctMethod.getName()+"",  $args);"+            "}"+            "play.classloading.enhancers.ControllersEnhancer.ControllerInstrumentation.stopActionCall();"            );    }  catch  (Exception  e)  {  ...  } }
  • 18. Translation to human Before each method, add “If we’re already in an action call ctMethod.insertBefore(            "if(!play.classloading.enhancers. ControllersEnhancer.ControllerInstrumentation. isActionCallAllowed())  {"+            "play.mvc.Controller.redirect(""+ send a redirect            ctClass.getName().replace("$",  "")+ to this new            "."+ctMethod.getName()+"",  $args);"+ action            "}"+ to the browser            "play.classloading.enhancers. ControllersEnhancer.ControllerInstrumentation. stopActionCall();" ); and stop the original action.”
  • 20. #{extends  'main.html'  /} this is Groovy #{set  title:'PDFLess'  /} #{ifErrors} <div  class="error">Please  correct  the  errors  below.</div> #{/ifErrors} #{form  @Documents.create()}    <p>        <label  for="doc.url">Enter  the  URL  to  make  a  PDF  out  of:</label>        <input  type="text"  id="doc.url"  name="doc.url"  value="${params['doc.url']}"/>        #{ifError  'doc.url'}            <span  class="error">#{error  'doc.url'  /}</span>        #{/ifError}    </p> this is gross    <p>        <label  for="doc.email">Enter  the  email  address  to  send  your  PDF  to:</label>        <input  type="text"  id="doc.email"  name="doc.email"              value="${params['doc.email']}"/>        #{ifError  'doc.email'}            <span  class="error">#{error  'doc.email'  /}</span>        #{/ifError}    </p>    <input  type="submit"  id="doc.submit"  /> #{/form} but this is kind of awesome
  • 21. tags are just app/views/tags/textField.html <p> HTML + Groovy    <label  for="${_arg}">#{doBody  /}</label>    <input  type="text"  id="${_arg}"  name="${_arg}"  value="${params[_arg]}"/>    #{ifError  _arg}        <span  class="error">#{error  _arg  /}</span>    #{/ifError} </p> and are used like app/views/Documents/index.html built-in tags #{form  @Documents.create()}    #{textField  'doc.url'}Enter  the  URL  to  make  a  PDF  out  of:#{/textField}    #{textField  'doc.email'}        Enter  the  email  address  to  send  your  PDF  to:    #{/textField}    #{textField  'code'}Please  type  the  code  below:#{/textField}        <p>        <input  type="hidden"  name="randomID"  value="${randomID}"  />        <img  class="captcha"  src="@{Application.captcha(randomID)}"  />    </p>    <input  type="submit"  id="doc.submit"  value="Make  My  PDF"  /> #{/form}
  • 23. Jobs Jobs  let  you  perform   actions  outside  of  web   requests. They  take  the  place  of   startup  tasks,  cron  jobs,   and  can  be  used  instead   of  queues.
  • 24. Startup jobs package  jobs; import  play.jobs.Job; import  play.jobs.OnApplicationStart; @OnApplicationStart public  class  SeedDataJob  extends  Job  {    public  void  doJob()  {        loadSeedData("db/seed.yml");    } }
  • 25. Periodic jobs package  jobs; import  play.jobs.Job; import  play.jobs.Every; @Every("2h") public  class  TempDirCleanJob  extends  Job  {    public  void  doJob()  {        cleanUpOldPDFs();    } }
  • 27. Current modules • CRUD • Database  migrations • Authentication  &   • Sass authorization • Routes  via  annotations • Captcha  generation • Scala  &  Scalate • Code  coverage • Search • Google  App  Engine • Guice • Google  Web  Toolkit • Spring • Oauth • and  more!
  • 29. Integrated testing tools JUnit,  Selenium You  cannot  be  told  what  the  JUnit  is.
  • 31. This guy is a moron This guy is really nice
  • 32. Young, but shows promise The  mailing  list  for  Play  is   very  active,  and  new   modules  are  being  created   all  the  time. Reminds  me  of  Rails   around  1.0. Could  easily  blow  up;   could  easily  slowly  die  off.
  • 33. Awesome things we don’t have time for • Suspendable  requests • Easiest  Eile  uploads  ever • Bespin  module  to  code  your  application  inside  your   application • Automatic  type  extensions  in  templates • Simple  i18n
  • 34. Summary Play  comes  to  Java  with  as  few  preconceptions  as   possible. It  hooks  into  existing  Java  libraries  easily. It’s  surprisingly  fun  to  use  and  quick  to  develop  with. It’s  a  project  with  lots  of  room  for  individuals  to   make  a  difference. If  you  liked  this  talk,  please   http://playframework.org     consider  donating  to  the   Buy  Clinton  IntelliJ  fund.   http://crnixon.org My  free  tial  ends  in  3  days. @crnixon