Play! Framework for JavaEE Developers
Upcoming SlideShare
Loading in...5
×
 

Play! Framework for JavaEE Developers

on

  • 17,939 views

Play! Framework for JavaEE Developers

Play! Framework for JavaEE Developers
Presented in JavaTWO Taiwan @ Jul 29,2011

Statistics

Views

Total Views
17,939
Views on SlideShare
17,772
Embed Views
167

Actions

Likes
30
Downloads
549
Comments
2

8 Embeds 167

https://dia.log.pt 55
http://www.scoop.it 39
http://www.linkedin.com 36
https://twitter.com 23
http://tweetedtimes.com 6
http://twitter.com 5
https://si0.twimg.com 2
http://www.slashdocs.com 1
More...

Accessibility

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Hello Smallufo,

    Can you please help me how to use 2 databases in a single play application.
    I am using MYSQL and POSTGRESQL

    Thanks in advance.
    Are you sure you want to
    Your message goes here
    Processing…
  • examples do not work
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Play! Framework for JavaEE Developers Play! Framework for JavaEE Developers Presentation Transcript

  • JavaTWO專業技術大會
  • Play!Framework for JavaEE Developers
  • Who am I
    Exp : JSP/Servlet , Tapestry , Spring/Hibernate/JavaEE , Wicket , Android , Grails , Play! Framework
    http://gplus.to/smallufo
    http://twitter.com/smallufo
    smallufo@gmail.com
  • Web Framework Experiences
    • JSP/Servlet
    • Too old , cumbersome
    •  Tapestry (old experience, T5 is very good)
    • Complicated , evolution/deprecation too fast
    •  Wicket
    • Too elaborate , fail to see the wood for the trees
    • May lead to over-engineered architecture for OO-purism
    • Spend too much time refactoring
    • Maybe web component reuse is just a myth
    • Grails (old experience, without Groovy++)
    • Slow
    • Too many inconsistent DSLs (URL-mapping , DB , logging...)
    • Not Java , IDE unfriendly
  • Conventional JavaEE Stack
  • Conventional JavaEE Stack
    • Want standard page ?
    • Let's define JSP/JSTL (JSR-53)
    • And let Apache (or others) to implement Jakata Taglibs
    •  Want web components ?
    • Let's define Java Server Faces (JSR-127)
    • And let communities to implement MyFaces, PrimeFaces, ICEfaces...
    • Want restful ?
    • Let's define JAX-RS (JSR-311)
    • And let communities to implement CXF, Jersey, RESTEasy, Wink...
  • Conventional JavaEE Stack
    • Lots of ...
    • Specs
    • Implementations
    • Configurations
    • Tedious , Error-prone
    • Unless you use a full-fledged JavaEE Server (Glassfish or WebLogic ...)
    • Layered
    • Defined by Standards/Specs
    • Assembled by Interfaces
    OVER ARCHITECTED
  • Play’s App Looks Like ?
    conf/routes
    app/App.java
    GET /hello App.hello
    public class App extends Controller {
    public static void hello() {
    User user = User.findById(1L);
    render(user);
    }
    }
    views/App/hello.html
    Hello World : ${user.name}
    http://localhost:9000/hello
  • And Play is ...
    • A full stack framework (platform)
    • Totally independent of JavaEE environment
    • RESTful
    • No session
    • Stateless
    • Pure server side
    • Similar to Rails / Django
    • Rich domain model
    • Unlike JavaEE's anemic domain model
  • Full Stack Framework?
    • Bundles compiler , embedded server , hibernate , logger , test runner , email , groovy template engine, scala...
    • Can be packaged as a WAR and deployed to servlet containers
  • Play! A Glued Pure Web Framework
    Shallow!
    Most libs are directly 
    exposed to Play! & replaceable
    Where Magic Happens
  • Play! URLs are RESTful & SEO-friendly
    UGLY
    GRACEFUL
    • /car/1
    • /listcar/page1/count10
    • /listcar/page1?count=10
    • /listcar/page/1/count/10
    • /car.jsp?id=1
    • /listcars.jsp?page=1&count=10
    • http://www.facebook.com/ photo.php? fbid=2121715487568& set=o.172881386106136& type=1&theater
  • Play is not based on Servlet
    Many java server side frameworks (Spring MVC / Struts / Wicket / JSF / Tapestry ...) are based on servlets, but Play! is not!
    You cannot do such things :
    • session.setAttribute("user" , user); 
    • Use HttpSessionListener to count sessions (online users)
    • ServletFilter
    • Servlet-related securities
    •  @ServletSecurity
    •  <auth-constraint /> , <security-role />
  • Play! is Stateless
    • Play! shares nothing between each request!
    • The most important feature you have to keep in mind
    • No session!
    • Session is the source of all problems of the JavaEE platform!
    • Session replication 
    • Sticky session
    • Play uses (delegates to) memcached to eliminates such headaches
  • Play's Informative Error Page
  • Play's Domain Objects Are Rich
    Play
    *Spring promoted JavaEE
    User extends Model
    User extends Object
    UserDao.java
    • save(User u)
    User u = new User(...)
    u.save()
    UserDaoImpl implements UserDao
    • void setEntityManager(...)
    • save(User u)
    Client.java
    @Inject UserDao userDao
    User u = new User(...)
    userDao.save(u)
    VS
    *JPA advocates eliminating the DAO layer
  • Play's Architecture
    $ play new myproj
    myproj
    app
    controllers
    models
    conf
    application.conf
    views
    messages
    lib
    foo
    routes
    modules
    bar
    public
    images
    javascripts
    src
    stylesheets
  • conf/application.conf
    application.name=myapp
    application.mode=dev
    application.secret=OOXXOOXXOOXXOOXXOOXX
    http.port=9000
    java.source=1.6
    db.url=jdbc:mysql:...
    db.driver=com.mysql.jdbc.Driver
    db.user=user
    db.pass=pass
    hibernate.use_sql_comments=true
    hibernate.format_sql = true
    XForwardedSupport=127.0.0.1
  • Play's Architecture
    controllers
    app
    MyController.java
    public static void index()
    public static void show()
    models
    views
    MyController
    index.html
    foo
    show.html
    bar
  • conf/routes
    HTTP method
    URI Pattern
    Action
    GET    /                      App.index
    POST   /login                 App.login
    GET    /tag/{name}            App.tag(name)
    GET    /list/{page}           App.list(page)
    GET    /list/{<[0-9]+>page}   App.list(page)
    GET    /list/{page}/{count}   App.list(page,count)
    GET    /public/               staticDir:public
    *      /{controller}/{action} {controller}.{action}
  • Play's Request Life Cycle
  • Controllers and Redirections
    public class App extends play.mvc.Controller {
      public static void index() {
        render(); // views/App/index.html contains a login form
      }
      public static void login(String name,String passwd) {
        if(...) // success
          welcome();
        else   // failed
          index();
      }
      public static void welcome() {
        render();  // renders views/App/welcome.html 
      }
    }
  • Controller Interceptions
    public class App extends play.mvc.Controller {
      @play.mvc.Before(unless={“index”, “login”})
      public static void intercept() {
        if (session.get("uid")==null) 
          index();
      }
      public static void index() {...}
      public static void login(String name,Stringpasswd){..}
      public static void welcome() {...}
    }
  • Session : A Signed Cookie
    public static void login(String name , String passwd) {
      User u = ...
      if (...) {
        session.put("uid" , u.id);
      }
    }
    Only put index data to session
    Never store sensitive data
    signed, 
    uneditable!
  • Controller Revisited
    public static void show(Long uid, String type) {
      User u = ...
      if (type.equals("json") {
        renderJSON(u);   // provided by GSON
      } 
      else if (type.equals("xml") {
        renderXml(u);    // provided by XStream
      } 
      
      render(u);  
    }
    Why not continue rendering?
    play.mvc.Controller
    throw new RenderJson(jsonString);
    throw new RenderXml(xml);
    throw new RenderTemplate(...);
  • Controller and View
    controllers/App.java
    public static void showUser(Long uid) {
      User u = ...
      List<Car> cars = ...
      renderArgs.put("user",u);
      render(cars, company, job, ...);  
    }
    public static void showCar(Long id) {...}
    renderArgs.put(“cars”,car);
    renderArgs.put(“company”,company);
    renderArgs.put(“job”,job);
    same
    views/App/showUser.html
    Hello ${user.name} , these are your cars :
    #{list items:cars , as:'car'}
      #{a @showCar(car.id)} ${car.name} #{/a}
    #{/list}
    template tag
    @App.showCar(car.id)
  • How objects are passed to View !?
    controllers/App.java
    public static void showUser(Long uid) {
      renderArgs.put("user",u);
      render(cars , ...);  
    }
    !!??
    views/App/showUser.html
    ${user.name}
    #{list items:cars , as:'car'}
    Let's decompile it...
  • public static void showUser(Long uid)
    {
      play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.enter();
      play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.addVariable("uid", uid);
      if(!play.classloading.enhancers.ControllersEnhancer.ControllerInstrumentation.isActionCallAllowed())
      {
        Controller.redirect("controllers.App.showUser", new Object[] {
          uid
        });
      } else
      {
        play.classloading.enhancers.ControllersEnhancer.ControllerInstrumentation.stopActionCall();
        User u = (User)User.findById(uid);
        play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.addVariable("u", u);
        List cars = Car.all().fetch();
        play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.addVariable("cars", cars);
        Object obj = null;
        play.mvc.Scope.RenderArgs renderargs = null;
        renderargs = (play.mvc.Scope.RenderArgs)Java.invokeStatic(Desc.getType("Lplay/mvc/Scope$RenderArgs;"), "current");
        renderargs.put("user", u);
        render(new Object[] {
          cars
        });
      }
      break MISSING_BLOCK_LABEL_120;
      Exception exception;
      exception;
      Object obj1 = null;
      play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.exit();
      throw exception;
      Object obj2 = null;
      play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.exit();
      return;
    }
  • Who Modifies My Code !?
    Who ?
    • Play's custom classloader & JDT & javassist
    When ?
    • DEV mode : When you modify the code and reload the page
    • PROD mode : App's start time
    How ?
    • At compile time , some classes are enhanced by play.classloading.enhancers.* (powered by javassist)
    • ContinuationEnhancer
    • ControllerEnhancer
    • PropertiesEnhancer
    • LocalvariablesNamesEnhancer, ...
    Impact ?
    • Rapid development time !
  • View Template
    object path
    ${objectname}
    ${object.property}
    implicit objects
    ${params.userId}
    ${session.userId}
    ${request.userId}
    built-in tags
    #{if} ... #{/if} #{else}... #{/else}
    #{list items:users , as:'user'}
      ${user.name}
    #{/list}
    #{a @App.showUser(user.id)} show user #{/a}
    #{form @App.login() } ... #{/form}
  • Rich Domain Object Model
    package models;
    @javax.persistence.Entity
    public class User extends play.db.jpa.Model {
      public String username;
      public String password;
    }
    Support JPA's annotations : @Column , @ManyToOne , @OneToMany ...
    NO more getters & setters... Great !
  • Rich Domain Object Model
    BUT...
    The underlayer is hibernate & hibernate needs getter/setter
    Again...
    Who modifies my model ?
  • Rich Domain Object Model
    In Fact...
        Your model still contains getter/setter , modified by Play's custom classloader & JDT & javassist
    User.java
    public String getUsername() {
      return "overridden";
    }
    ${user.username} will be ??
  • Rich Domain Object
    User u = User.findById(1);
    User u = User.find("byUsernameAndPassword", username , password).first();
    User u = User.find("select u from User u where u.username = :username and u.password = :password")
      .bind("username",username)
      .bind("password",password)
      .first();
    List<User> users = User.all().fetch();
    User.em().createQuery(...);
  • Validations : Controller
    #{form @App.login()}
    <p>
    username
    <input type=“text” name=“username” value=“${flash.username}” />
    <span class=“error”>#{error 'username'/}</span>
    </p>
    <p>
    password
    <input type=“password” name=“password” value=“${flash.password}” />
    <span class=“error”>#{error 'password'/}</span>
    </p>
    <input type="submit" value=“Login" />
    <span class=“error”>#{error ‘other'/}</span>
    #{/form}
    username
    請輸入帳號
    password
    請輸入密碼
    Login
    帳號或密碼輸入錯誤
  • Validations : Controller
    public static void login(
    @Required(message ="請輸入帳號")String username,
    @Required(message ="請輸入密碼")String password) {
    User user = User.login(username , password);
    if (validation.hasErrors()){
    params.flash(); // add parameters to flash scope
    validation.keep(); // keeps the errors
    flash.error(validation.errors().get(0).toString());
    render(“pleaseLogin.html”);
    }
    flash.success(“welcome : “ + user.username);
    render();
    }
  • Validation : Model
    public class User extends Model {
    @Required @MinSize(6)
    public username;
    @Required @MinSize(6)
    public password;
    public static User login(String username , String password) {
    Validation validation = Validation.current();
    User user = ...
    validation.isTrue(user!=null)
    .key(“other”).message(“帳號或密碼輸入錯誤”);
    return user;
    }}
    Will @MinSize affect login() ?
  • Validations
    @Required , @Min , @Max , @MinSize , @MaxSize , @Range , @Email , @URL ...
    Custom validation annotation
    extends AbstractAnnotationCheck & implements isSatisfied()
    i18n messages
    /conf/messages
  • Cache
    Conventional JavaEE's Way
    public User getUser(String name) {
      Session s = (Session)em.getDelegate();
      Criteria c = s.createCriteria(User.class);
      c.add(Restrictions.eq("username",name);
      c.setMaxResults(1);
      c.setCacheable(true);
      if (c.uniqueResult() == null)
        return null;
      return (User) c.uniqueResult();
    }
  • Cache
    Play's Way : Not In Favor of 2nd Level
    public static User getUser(String name) {
      String key="username_"+name;
      User user = Cache.get(key,User.class);
      if (user != null)
        return user;
      user = User.find("byUsername",name).first();
      Cache.set(key,user,"30mn");
      return user;
    }
  • Cache - Problem !
    User.java {
      public static User getUser(Long id) {
        String key = "userId_"+id;
        ...
      }
      public static List<User> getUsers(Long page, int cnt) {
        String key="users_"+page+"_"+cnt;
        ...
      }
    }
    public interface UserDao.java {
    public User getUser(Long id);
    public List<User> getUsers(Long page, int cnt);
    }
  • Conventional JavaEE's Way
    User u1 = userDao.getUsers(1,10).get(0);
    User u2 = userDao.get(1L);
    assertTrue(u1.equals(u2)); // PASSED
    u2.modifySomething(...);
    userDao.save(u2);
    User u3 = userDao.getUsers(1,10).get(0);
    assertTrue(u3.equals(u2)); // PASSED
    Play's Way
    User u1 = User.getUsers(1,10).get(0);
    User u2 = User.getUser(1L);
    assertTrue(u1.equals(u2)); // PASSED
    u2.modifySomething(...);
    u2.save();
    User u3 = User.getUsers(1,10).get(0);
    assertTrue(u3.equals(u2)); // FAILED!
  • Cache Problem : Reason
    users_1_10
    cache key :
    TIME
    u
    u
    u
    u
    u
    u
    u
    u
    u
    u
    u1
    userId_1
    cache key :
    u
    modified / updated
    u2
    users_1_10
    cache key :
    ?
    u
    u
    u
    u
    u
    u
    u
    u
    u
    u3
  • Cache - Problem! How to Solve It ?
    • Ignore it , accept it
    • Never cache sensitive data
    • Two phase list retrieval
    • Cache object ids instead of objects
    public static List<User> getUsers(Long page, int cnt) {
      String key="users_"+page+"_"+cnt;  
      List<Long> userIds = 
        User.find("select u.id from User u)
        .fetch(page,cnt);
      Cache.set(key, userIds, "1mn");
      // iterate each id in result and query cache or fetch
    }
  • Cache : Wait... I saw Model.em() ?
    How about get underlaying Hibernate’s session and setCacheable(true) ?
    Session s = (Session) User.em().getDelegate();
    Critieria c = s.createCriteria(...);
    c.add(... criterions ... );
    c.setCacheable(true);
  • Play's Module System
  • Module : CRUD
    package models;
    public class User extends Model { ... }
    package controllers;
    public class Users extends CRUD { ... }
    Cars , Photos , Logs
    even ...
    Boxs , Buss, Kisss
  • Module GAE + Module Siena
    public class User extends siena.Model { 
      public String uid;
      public static User getUser(String uid) {
        return User.all(User.class).filter("uid",uid).get();
      }
      public static User getUsers(int page , int count) {
        return User
          .all(User.class)
          .fetch(count,(page-1)*count);
      }
    }
    Don't forget war/WEB-INF/appengine-web.xml
    $ play gae:deploy 
    Done !
  • Play on GAE
    • Simplest way to deploy Java Apps on GAE !
    • Shortest cold start time
    • Less than 10 seconds
    • Cache is wrapped to GAE's memcache
    • Mail is wrapped to GAE's mail service
  • Issue : Portal-like Page
    • Many blocks query DB in every page
    • Passing these query results in every action is cumbersome
    • Solution : @Before and renderArgs.put()
    @Before
    static void addDefaults() {
      renderArgs.put("brands", Brand.all().fetch());
      renderArgs.put("forums", Forum.getAll()); 
      renderArgs.put("tags" , Tag.getAll());
    }
  • Issue : High Availability
    <VirtualHost *:80>
     ServerName myapp.com
     <Location /balancer-manager>
      SetHandler balancer-manager
      Order Deny,Allow
      Deny from all
      Allow from .myapp.com
     </Location>
     <Proxy balancer://mycluster>
       BalancerMember http://localhost:9002
       BalancerMember http://localhost:9003 status=+H
     </Proxy>
     <Proxy *>
       Order Allow,Deny
       Allow From All
     </Proxy>
     ProxyPreserveHost on
     ProxyPass /balancer-manager !
     ProxyPass / balancer://mycluster/
     ProxyPassReverse / http://localhost:9002/
     ProxyPassReverse / http://localhost:9003/
    </VirtualHost>
    Apache Web Server
    localhost:9002
    localhost:9003
    Same directory structures & application.secret , only different http.port
  • Issue : Action Burst
    controllers/App
    controllers/App
    login()
    logout()
    register()
    myaccount()
    mybooks()
    index()
    page()
    search()
    showbook()
    login()
    logout()
    register()
    myaccount()
    mybooks()
    index()
    page()
    search()
    showBook()
    listUsers()
    editUser()
    editBook()
    controllers/Admin
    listUsers()
    editUser()
    editBook()
  • Issues : Validation Dilemma
    Validation in controllers ?
    validation in models ?
    or hybrid ?
  • Issue : DI
    Dependency Injection is not so useful in Play’s Rich Domain Object environment
  • Case Study : VAGTW.COM
    VW/Audi Car Problem Stats
  • vagtw.com Data Model
  • VAGTW.com development
    • Dev in one month (Since 2010/4/1)
    • Modified from Play's YABE sample
    • 10 days learning Play! & modeling & coding
    • 20 days tuning HTML & CSS(3)
    • 2010/5/1 Debut
    • Peak 4x users in 15 mins
    • Indexed by Google in 2 days
    • Indexed by Yahoo in 2 weeks
    • Indexed by Bing after 2 months
  • Conclusions
    IS ...
    • A Glued Pure Web Framework
    • Loose
    • Shallow
    • non-Standardized
    • Page-based
    • Stateless
    IS-NOT ...
    • A Java library
    • Full-Fledged JavaEE stack
    • Standard
    • Strict
    • Defined-assembled by interfaces
    • Component-based
    • Stateful (Session-aware)
  • Conclusions : Use Play! If You...
    Have to prototype or build something quickly
    Don’t want to buy high-priced Java application servers
    Are not so OO-purism, feel OK without interfaces
    Many Play’s “hook” are not enforced by abctract mathods or interfaces
    Know JavaScript & other JS frameworks
    You can build slick UIs without sluggish server-state implementation responses (Wicket/JSF...)
    Feel OK about object inconsistences in cache
    Want to develop GAE apps
    Your team have a strong mendiator
    Because programming in play is too unrestrained
    Want to learn Scala
  • 小心
    回不去了