Having Fun with Play


Published on

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Having Fun with Play

  1. 1. Having Fun withor, how a perfectly normaldynamic language developerfell in love with Java Clinton R. Nixon, Viget Labs
  2. 2. i am a robot comeAbout Me from the year 2082 in order to prevent java from becoming stale and borinG
  3. 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. 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. 5. Our example app: PDFlessA  way  to  build  PDFs  using  simple  tools:  HTML  +  CSS.Good  candidate  for  a  system  like  Play.  Uses  FlyingSaucer,  a  Java  library.
  6. 6. Starting our appcnixon@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!
  7. 7. Our model
  8. 8. Creating our modelpackage  models;import  play.data.validation.Email; app/models/Document.javaimport  play.data.validation.Required; public fieldsimport  play.data.validation.URL;import  play.db.jpa.Model;import  javax.persistence.Entity;@Entitypublic  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. 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. 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. 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
  12. 12. Our controller
  13. 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. 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  pageGET          /                                                                              Documents.indexGET          /documents                                                            Documents.indexPOST        /documents                                                            Documents.create#  Map  static  resources  from  the  /app/public  folder  to  their  pathsGET          /public/                                                                staticDir:public#  Catch  all*              /{controller}/{action}                                    {controller}.{action} sigh - but it is helpful with rapid development
  15. 15. Live demonstration
  16. 16. In case you didn’t catch that• No  compiling• Awesome  error  pages• Request  params  bind  to   controller  method   params
  17. 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. 18. Translation to humanBefore 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.”
  19. 19. Our view
  20. 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. 21. tags are justapp/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 likeapp/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}
  22. 22. Processing jobs
  23. 23. JobsJobs  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. 24. Startup jobs package  jobs;import  play.jobs.Job;import  play.jobs.OnApplicationStart;@OnApplicationStartpublic  class  SeedDataJob  extends  Job  {    public  void  doJob()  {        loadSeedData("db/seed.yml");    }}
  25. 25. Periodic jobs package  jobs;import  play.jobs.Job;import  play.jobs.Every;@Every("2h")public  class  TempDirCleanJob  extends  Job  {    public  void  doJob()  {        cleanUpOldPDFs();    }}
  26. 26. Using modules
  27. 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!
  28. 28. Testing this thing
  29. 29. Integrated testing toolsJUnit,  SeleniumYou  cannot  be  told  what  the  JUnit  is.
  30. 30. Community
  31. 31. This guy is a moronThis guy is really nice
  32. 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. 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. 34. SummaryPlay  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.orgMy  free  tial  ends  in  3  days. @crnixon