61
JavaTWO 專專專專專專

Play! Framework for JavaEE Developers

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: Play! Framework for JavaEE Developers

JavaTWO 專業技術大會

Page 2: Play! Framework for JavaEE Developers

Play!Framework for JavaEE Developers

Page 3: Play! Framework for JavaEE Developers

Who am I

Exp : JSP/Servlet , Tapestry , Spring/Hibernate/JavaEE , Wicket , Android , Grails , Play! Framework

http://gplus.to/smallufohttp://twitter.com/[email protected]

Page 4: Play! Framework for JavaEE Developers

Web Framework Experiences• JSP/Servlet

o Too old , cumbersome•  Tapestry (old experience, T5 is very good)

o Complicated , evolution/deprecation too fast•  Wicket

o Too elaborate , fail to see the wood for the treeso May lead to over-engineered architecture for OO-

purism Spend too much time refactoring

o Maybe web component reuse is just a myth• Grails (old experience, without Groovy++)

o Slowo Too many inconsistent DSLs (URL-mapping , DB ,

logging...)o Not Java , IDE unfriendly

Page 5: Play! Framework for JavaEE Developers

Conventional JavaEE Stack

Page 6: Play! Framework for JavaEE Developers

Conventional JavaEE Stack• Want standard page ?

o Let's define JSP/JSTL (JSR-53)o And let Apache (or others) to implement

Jakata Taglibs•  Want web components ?

o Let's define Java Server Faces (JSR-127)o And let communities to implement MyFaces,

PrimeFaces, ICEfaces...• Want restful ?

o Let's define JAX-RS (JSR-311)o And let communities to implement CXF, Jersey,

RESTEasy, Wink...

Page 7: Play! Framework for JavaEE Developers

Conventional JavaEE Stack• Lots of ...

o Specso Implementationso Configurations

• Tedious , Error-proneo Unless you use a full-fledged JavaEE

Server (Glassfish or WebLogic ...)• Layered

o Defined by Standards/Specso Assembled by Interfaces

OVER ARCHITECTED

Page 8: Play! Framework for JavaEE Developers
Page 9: Play! Framework for JavaEE Developers

Play’s App Looks Like ?

views/App/hello.html

Hello World : ${user.name}

conf/routes

GET /hello App.hello

http://localhost:9000/hello

app/App.javapublic class App extends Controller { public static void hello() { User user = User.findById(1L); render(user); }}

Page 10: Play! Framework for JavaEE Developers

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

o Unlike JavaEE's anemic domain model

Page 11: Play! Framework for JavaEE Developers

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

Page 12: Play! Framework for JavaEE Developers

Play! A Glued Pure Web Framework

Where Magic Happens

Shallow!

Most libs are directly exposed to Play! & replaceable

Page 13: Play! Framework for JavaEE Developers

Play! URLs are RESTful & SEO-friendly

• /car.jsp?id=1• /listcars.jsp?

page=1&count=10• http://www.facebook.com/

photo.php? fbid=2121715487568& set=o.172881386106136& type=1&theater

• /car/1• /listcar/page1/count10• /listcar/page1?count=10• /listcar/page/1/count/10

UGLY GRACEFUL

Page 14: Play! Framework for JavaEE Developers

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

o  @ServletSecurityo  <auth-constraint /> , <security-role />

Page 15: Play! Framework for JavaEE Developers

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!o Session replication o Sticky session

• Play uses (delegates to) memcached to eliminates such headaches

Page 16: Play! Framework for JavaEE Developers

Play's Informative Error Page

Page 17: Play! Framework for JavaEE Developers

Play's Domain Objects Are Rich*Spring promoted JavaEE Play

User extends Object

UserDao.java• save(User u)

UserDaoImpl implements UserDao• void setEntityManager(...)• save(User u)

Client.java@Inject UserDao userDaoUser u = new User(...)userDao.save(u)

User u = new User(...)u.save()

User extends Model

VS*JPA advocates eliminating the DAO layer

Page 18: Play! Framework for JavaEE Developers

Play's Architecture

myproj

app

conf

lib

modules

public

application.conf

messages

routes

controllers

models

foo

views

src

images

javascripts

stylesheets

bar

$ play new myproj

Page 19: Play! Framework for JavaEE Developers

conf/application.confapplication.name=myappapplication.mode=devapplication.secret=OOXXOOXXOOXXOOXXOOXXhttp.port=9000java.source=1.6

db.url=jdbc:mysql:...db.driver=com.mysql.jdbc.Driverdb.user=userdb.pass=pass

hibernate.use_sql_comments=truehibernate.format_sql = true

XForwardedSupport=127.0.0.1

Page 20: Play! Framework for JavaEE Developers

Play's Architecture

app controllers

models

foo

views

bar

MyController.java

MyController

public static void index()public static void show()

index.html

show.html

Page 21: Play! Framework for JavaEE Developers

conf/routes

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}

HTTP method

URI Pattern Action

Page 22: Play! Framework for JavaEE Developers

Play's Request Life Cycle

Page 23: Play! Framework for JavaEE Developers

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   }}

Page 24: Play! Framework for JavaEE Developers

Controller Interceptionspublic 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,String passwd){..}

  public static void welcome() {...}}

Page 25: Play! Framework for JavaEE Developers

Session : A Signed Cookiepublic static void login(String name , String passwd) {  User u = ...  if (...) {    session.put("uid" , u.id);  }}

signed, uneditable!

Only put index data to sessionNever store sensitive data

Page 26: Play! Framework for JavaEE Developers

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.Controllerthrow new RenderJson(jsonString);throw new RenderXml(xml);throw new RenderTemplate(...);

Page 27: Play! Framework for JavaEE Developers

Controller and View

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) {...}

Hello ${user.name} , these are your cars :

#{list items:cars , as:'car'}  #{a @showCar(car.id)} ${car.name} #{/a}#{/list}

views/App/showUser.html

controllers/App.java

@App.showCar(car.id)

template tag

renderArgs.put(“cars”,car);renderArgs.put(“company”,company);renderArgs.put(“job”,job);

same

Page 28: Play! Framework for JavaEE Developers

How objects are passed to View !?

public static void showUser(Long uid) {  renderArgs.put("user",u);  render(cars , ...);  }

${user.name}#{list items:cars , as:'car'}

!!??

Let's decompile it...

controllers/App.java

views/App/showUser.html

Page 29: Play! Framework for JavaEE Developers

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;}

Page 30: Play! Framework for JavaEE Developers

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 !

Page 31: Play! Framework for JavaEE Developers

View Template

${objectname}${object.property}

object path

implicit objects${params.userId}${session.userId}${request.userId}

#{if} ... #{/if} #{else}... #{/else}#{list items:users , as:'user'}  ${user.name}#{/list}#{a @App.showUser(user.id)} show user #{/a}#{form @App.login() } ... #{/form}

built-in tags

Page 32: Play! Framework for JavaEE Developers

Rich Domain Object Model

package models;

@javax.persistence.Entitypublic 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 !

Page 33: Play! Framework for JavaEE Developers

Rich Domain Object Model

BUT...The underlayer is hibernate & hibernate needs getter/setter

Again...Who modifies my model ?

Page 34: Play! Framework for JavaEE Developers

Rich Domain Object Model

In Fact...    Your model still contains getter/setter , modified by Play's custom classloader & JDT & javassist

User.javapublic String getUsername() {  return "overridden";}

${user.username} will be ??

Page 35: Play! Framework for JavaEE Developers

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(...);

Page 36: Play! Framework for JavaEE Developers

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

請輸入帳號

請輸入密碼

帳號或密碼輸入錯誤

Page 37: Play! Framework for JavaEE Developers

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();}

Page 38: Play! Framework for JavaEE Developers

Validation : Modelpublic 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() ?

Page 39: Play! Framework for JavaEE Developers

Validations

• @Required , @Min , @Max , @MinSize , @MaxSize , @Range , @Email , @URL ...

• Custom validation annotation– extends AbstractAnnotationCheck & implements

isSatisfied()

• i18n messages– /conf/messages

Page 40: Play! Framework for JavaEE Developers

CacheConventional 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();}

Page 41: Play! Framework for JavaEE Developers

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;}

Page 42: Play! Framework for JavaEE Developers

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);}

Page 43: Play! Framework for JavaEE Developers

Conventional JavaEE's WayUser u1 = userDao.getUsers(1,10).get(0);User u2 = userDao.get(1L);assertTrue(u1.equals(u2)); // PASSEDu2.modifySomething(...);userDao.save(u2);

User u3 = userDao.getUsers(1,10).get(0);assertTrue(u3.equals(u2)); // PASSED

Play's WayUser u1 = User.getUsers(1,10).get(0);User u2 = User.getUser(1L);assertTrue(u1.equals(u2)); // PASSEDu2.modifySomething(...);u2.save();

User u3 = User.getUsers(1,10).get(0);assertTrue(u3.equals(u2)); // FAILED!

Page 44: Play! Framework for JavaEE Developers

Cache Problem : Reason users_1_10cache key :

u u u u u u u u u u

u1

userId_1cache key :

u

u2

modified / updated

users_1_10cache key :

? u u u u u u u u u

u3

TIME

Page 45: Play! Framework for JavaEE Developers

Cache - Problem! How to Solve It ?• Ignore it , accept it• Never cache sensitive data• Two phase list retrieval

o 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}

Page 46: Play! Framework for JavaEE Developers

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);

Page 47: Play! Framework for JavaEE Developers

Play's Module System

Page 48: Play! Framework for JavaEE Developers

Module : CRUD

package models;public class User extends Model { ... }

package controllers;public class Users extends CRUD { ... }

Cars , Photos , Logseven ...Boxs , Buss, Kisss

Page 49: Play! Framework for JavaEE Developers

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 !

Page 50: Play! Framework for JavaEE Developers

Play on GAE

• Simplest way to deploy Java Apps on GAE !• Shortest cold start time

o Less than 10 seconds• Cache is wrapped to GAE's memcache• Mail is wrapped to GAE's mail service

Page 51: Play! Framework for JavaEE Developers

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()

@Beforestatic void addDefaults() {  renderArgs.put("brands", Brand.all().fetch());  renderArgs.put("forums", Forum.getAll());   renderArgs.put("tags" , Tag.getAll());}

Page 52: Play! Framework for JavaEE Developers

Issue : High Availability

localhost:9002 localhost:9003

Apache Web Server

<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>

Same directory structures & application.secret , only different http.port

Page 53: Play! Framework for JavaEE Developers

Issue : Action Burst

login()logout()

register()myaccount()mybooks()

index()page()

search()showBook()listUsers()editUser()editBook()

controllers/Appcontrollers/Applogin()

logout()register()

myaccount()mybooks()

index()page()

search()showbook()

controllers/Admin

listUsers()editUser()editBook()

Page 54: Play! Framework for JavaEE Developers

Issues : Validation Dilemma

Validation in controllers ?

validation in models ?

or hybrid ?

Page 55: Play! Framework for JavaEE Developers

Issue : DI

• Dependency Injection is not so useful in Play’s Rich Domain Object environment

Page 56: Play! Framework for JavaEE Developers

Case Study : VAGTW.COMVW/Audi Car Problem Stats

Page 57: Play! Framework for JavaEE Developers

vagtw.com Data Model

Page 58: Play! Framework for JavaEE Developers

VAGTW.com development

• Dev in one month (Since 2010/4/1)o Modified from Play's YABE sampleo 10 days learning Play! & modeling & codingo 20 days tuning HTML & CSS(3)

• 2010/5/1 Debuto Peak 4x users in 15 minso Indexed by Google in 2 dayso Indexed by Yahoo in 2 weekso Indexed by Bing after 2 months

Page 59: Play! Framework for JavaEE Developers

Conclusions

IS ...• A Glued Pure Web

Frameworko Looseo Shallowo non-Standardized

• Page-based• Stateless

IS-NOT ...• A Java library• Full-Fledged JavaEE stack

o Standardo Stricto Defined-assembled by

interfaces• Component-based• Stateful (Session-aware)

Page 60: Play! Framework for JavaEE Developers

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

Page 61: Play! Framework for JavaEE Developers

回不去了小心