Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Taming the Spaghetti: Rich Web Applications With Errai Christian Sadilek (@csadilek) Lincoln Baxter III (@lincolnthree) JBoss / Red Hat Another Java Web Framework? Photo by: darkuncle From: Flickr What has changed? Fast execution Consistent runtime Spec compliant More application logic in the browser Photo by: Kyle Nishioka (madmarv00) From: Flickr Large talent pool Great tooling Amazing ecosystem Photo by: jeffeaton From: Flickr Java-to-JavaScript Compiler Cross-browser support Forc ed into Serv er-s ide Heavy up-front optimizations prog ram m com atic U pilat ion Development Mode I co mpo nen at ru ntim e Code and Refresh development Debug client and server as Java code in your IDE ts Project mantras Declarative rocks Boilerplate sucks Code it once Project features Java EE 6 APIs Unintrusive marshalling Peer to peer communication Example project layout src/main/java HelloWorld.gwt.xml client HelloWorldClient.java shared HelloWorldEntity.java server HelloWorldService.java src/main/webapp HelloWorld.html WEB-INF web.xml src/main/resources ErraiApp.properties } Compiled to JavaScript Photo by: CORE-Materials From: Flickr CDI in the browser Injecting and firing events @Inject @Updated private Event<Document> updatedDocEvent; ... button.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { updatedDocEvent.fire(document); } }); CDI in the browser Observing events public void onUpdatedDocument(@Observes @Updated Document doc) { // received updated document } On the client On the server Or both (events are sent across the wire) Uniform programming model on the client and server CDI in the browser Producers @Produces @Supported public MyBaseWidget createWidget() { return (Canvas.isSupported()) ? new MyHtml5Widget() : new MyDefaultWidget(); } @Inject @Supported private MyBaseWidget widget; Remote Calls Errai RPC @Remote public interface HappyService {...} shared @Service public class HappyServiceImpl implements HappyService { public boolean isEveryoneHappy() { return true; // this could be a lie! } } server @Inject private Caller<HappyService> happyService; ... happyService.call(new RemoteCallback<Boolean>() { public void callback(Boolean response) { // process response } }).isEveryoneHappy(); client Browser Browser Web Sockets Browser Long Polling Long Polling Server Long Polling Long Polling Browser Browser Web Sockets Browser Remote Calls Errai JAX-RS @Path("customers") public interface CustomerService { @POST @Consumes("application/json") @Produces("text/plain") public long createCustomer(Customer customer); } @Inject private Caller<CustomerService> customerService; ... customerService.call(new RemoteCallback<Long>() { public void callback(Long response) { Window.alert(response); } }).createCustomer(customer); Marshalling @Portable on simple entities @Portable public class Person { private String name; private int age; public Person() {} public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } ... } Marshalling @Portable on simple entities @Portable public class Person { private String name; private int age; public Person() {} public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } ... } Marshalling @Portable on immutable entities @Portable public class Person { private final String name; private final int age; private Person(String name, int age) { this.name = name; this.age = age; } public static Person create(@MapsTo("name") String name, @MapsTo("age") int age) { return new Person(name, age); } public String getName() { return name; } public int getAge() { return age; } } Demo! JPA in the browser public class RecordStoreView extends Composite { ... @Inject private EntityManager em; public void onSaveButtonClicked(ClickEvent event) { em.persist(album); em.flush(); } } JPA in the browser @Entity @NamedQuery(name="selectAlbumByName", query="SELECT a FROM Album a WHERE a.name=:name") public class Album { @GeneratedValue @Id private Long id; private Date releaseDate; private String name; @ManyToOne private Artist artist; } Errai UI - HTML5 <!DOCTYPE html> <link href="css/bootstrap.css" rel="stylesheet"> <form data-field="form"> <legend>Log in to your account</legend> <label for="username">Username</label> <input data-field="username" id="username" type="text" placeholder="Username"> <label for="password">Password</label> <input data-field="pass" id="password" type="password" placeholder="Password"> <button data-field="submit">Log in</button> <button>Cancel</button> </form> Errai UI - HTML5 @Templated public class LoginForm extends Composite { @Inject @DataField private TextBox username; @Inject @DataField("pass") private PasswordTextBox password; @DataField private Button submit = new Button(); @EventHandler("submit") private void onLogin(ClickEvent e) { // send login request } } Errai UI - Data Binding @Templated public class LoginForm extends Composite { @Inject @Bound @DataField private TextBox username; @Inject @Bound @DataField("pass") private PasswordTextBox password; @Inject @Model private User user; @EventHandler("submit") private void onLogin(ClickEvent e) { login(user); } } Errai UI - Navigation @Templated public class LoginForm extends Composite { ... @Inject private TransitionTo<WelcomePage> welcomePage; public onLoginSuccessful() { welcomePage.go() } } @Templated @Page public class WelcomePage extends Composite { } Behind the Scenes: How does it all work? java.lang.reflect Class.forName() Deferred Binding A feature of the GWT compiler that allows to replace or generate types at runtime <replace-with class="com.google.gwt.user.client.ui.impl.PopupImplMozilla"> <when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl"/> <when-property-is name="user.agent" value="gecko1_8"/> </replace-with> <generate-with class="org.jboss.errai.bus.rebind.RpcProxyLoaderGenerator"> <when-type-assignable class="org.jboss.errai.bus.client.framework.RpcProxyLoader"/> </generate-with> Don’t write this by hand! // SELECT a FROM Album a WHERE a.name=:name super.namedQueries.put("selectAlbumByName", new TypedQueryFactory(this, Album.class, new ErraiParameter[] { new ErraiParameter("name", 0, String.class) }) { protected TypedQuery createQuery() { return new ErraiTypedQuery(entityManager, actualResultType, parameters) { protected Comparator getComparator() { return null; } public boolean matches(JSONObject candidate) { return Comparisons.nullSafeEquals( JsonUtil.basicValueFromJson( candidate.get("name"), String.class), getParameterValue("name")); } }; } }); Photo by: Tim Bartel (avatar-1) From: Flickr Photo by: Michelle Lowry From: Wikimedia Commons Future Work Data synchronization (JPA and OT) Security Framework Errai mobile JRebel support Photo by: Mike Lewis (pescatello) From: Flickr Get in touch! Website: http://jboss.org/errai IRC: #errai on freenode Twitter: @jbosserrai Photo by: CHIN.DENG From: Flickr http://github.com/errai