C:\Play>play new eazybookmark ~ _ _ ~ _ __ | | __ _ _ _| | ~ | '_ \| |/ _' | || |_| ~ | __/|_|\____|\__ (_) ~ |_| |__/ ~ ~ play! 1.1, http://www.playframework.org ~ ~ The new application will be created in C:\Play\eazybookmark ~ What is the application name? [eazybookmark] ~ ~ OK, the application is created. ~ Start it with : play run eazybookmark ~ Have fun! ~The above Hello World video shows in depth how to install and use Play and create your first application.
C:\Play> play install gae .. .. C:\Play> play install sienaThe above commands will install latest version of these modules. If you are behind proxy than the above command may fails. Currently there is no way of setting up proxy with Play! (v1.1) so you can manually download gae and siena from its homepage and copy them in /modules directory of your Play!. Once the modules are installed successfully, you can add following lines in eazybookmark apps /conf/application.conf file.
# ---- Google app engine module ---- module.gae=${play.path}/modules/gae-1.4 # ---- Siena module ---- module.siena=${play.path}/modules/siena-1.3You may want to restart the Play application to reflect the changes of the modules. Once the application is restarted, the GAE module creates
/war/WEB-INF/appengine-web.xml
file. You need to update this file and add the application id of GAE. In our case we will have following data. File: /war/WEB-INF/appengine-web.xml <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>eazybookmark</application>
<version>1</version>
</appengine-web-app>
Code language: HTML, XML (xml)
You may want to replace eazybookmark with your own GAE Application ID. package models;
import siena.*;
public class User extends Model {
@Id
public Long id;
public String email;
public String name;
public Date created;
public Date modified;
static Query<User> all() {
return Model.all(User.class);
}
public static User findById(Long id) {
return all().filter("id", id).get();
}
public static User findByEmail(String email) {
return all().filter("email", email).get();
}
public User() {
super();
}
public User(String email) {
this.email = email;
}
public String toString() {
return email;
}
}
Code language: Java (java)
The User model has few attributes like email, name, created and modified which define a user. Few methods like findById()
and findByEmail()
select the User based on Id and email respectively. File: /app/model/Link.java package models;
import java.util.*;
import siena.*;
public class Link extends Model {
@Id(Generator.AUTO_INCREMENT)
public Long id;
public String url;
public String title;
public String description;
public Date created;
public Date modified;
@Filter("link")
public Query<LinkTag> linktags;
@Index("user_index")
public User user;
public Link(User user, String url) {
super();
this.url = url;
this.user = user;
}
public Link() {
super();
}
static Query<Link> all() {
return Model.all(Link.class);
}
public static Link findById(Long id) {
return all().filter("id", id).get();
}
public static List<Link> findByUser(User user) {
return all().filter("user", user).order("-created").fetch();
}
public static void addTag(Link link, Tag tag) {
LinkTag linkTag = new LinkTag(link, tag);
linkTag.insert();
}
public String toString() {
return url;
}
public List<Tag> findTagsByLink() {
List<Tag> tags = LinkTag.findByLink(this);
return tags;
}
public static void addTagsFromCSV(Link link, String tagcsv, User user) {
Collection<Tag> tags = null;
if(null != tagcsv || !tagcsv.equalsIgnoreCase("")) {
String [] tagArr = tagcsv.split(",");
tags = new ArrayList<Tag>();
for(String tagstr : tagArr) {
tagstr = play.templates.JavaExtensions.slugify(tagstr.trim()).trim();
if(null != tagstr && !tagstr.equals("")) {
Link.addTag(link, Tag.findOrCreateByName(tagstr,user));
}
}
}
}
}
Code language: Java (java)
The Link model class is similar to User. It has its own attributes such as url, title, description etc. Also note that Link model has few methods like findById()
, findTagsByLink()
, addTag()
, addTagsFromCSV()
etc. These methods are used from the controller to manipulate the link data. The Link has a many to many relationship with Tag. One link can have multiple tags attached to it. File: /app/model/Tag.java package models;
import siena.*;
import java.util.*;
public class Tag extends Model {
@Id(Generator.AUTO_INCREMENT)
public Long id;
public String name;
@Index("user_index")
public User user;
public static Query<Tag> all() {
return Model.all(Tag.class);
}
public static Tag findOrCreateByName(String name, User user) {
Tag tag = all().filter("user", user).filter("name", name).get();
if(null == tag) {
tag = new Tag();
tag.name = name;
tag.user = user;
tag.insert();
}
return tag;
}
public static Tag findById(Long id) {
return all().filter("id", id).get();
}
public static Tag findByName(String name, User user) {
return all().filter("user", user).filter("name", name).get();
}
public static List<Tag> findByUser(User user) {
return all().filter("user", user).fetch();
}
public String toString() {
return name;
}
}
Code language: Java (java)
The Tag model has one attribute tag name. This can be anything like travel, leisure, technology, news etc. Also note that each tag has one to one mapping with User. This is defined by @Index("user_index")
annotation with object User. The Tag has usual methods findById
, findByName
, findByUser
, findOrCreateByName
etc which is used from controller to manipulate the Tag data. File: /app/model/LinkTag.java package models;
import java.util.*;
import siena.*;
public class LinkTag extends Model {
@Id
public Long id;
@NotNull
@Column("link_id")
@Index("link_index")
public Link link;
@NotNull
@Column("tag_id")
@Index("tag_index")
public Tag tag;
public LinkTag(Link link, Tag tag) {
super();
this.link = link;
this.tag = tag;
}
public static Query<LinkTag> all() {
return Model.all(LinkTag.class);
}
public static List<Tag> findByLink(Link link) {
List<LinkTag> linkTags = all().filter("link", link).fetch();
List<Tag> tags = new ArrayList<Tag>();
for(LinkTag linkTag : linkTags) {
tags.add(Tag.findById(linkTag.tag.id));
}
return tags;
}
public static List<Link> findByTag(Tag tag) {
List<LinkTag> linkTags = all().filter("tag", tag).fetch();
List<Link> links = new ArrayList<Link>();
for(LinkTag linkTag : linkTags) {
links.add(Link.findById(linkTag.link.id));
}
return links;
}
public String toString() {
return link.toString() + " : " + tag.toString();
}
public static void deleteByLink(Link link) {
List<LinkTag> linkTags = all().filter("link", link).fetch();
if(null != linkTags) {
for(LinkTag linktag : linkTags) {
linktag.delete();
}
}
}
}
Code language: Java (java)
The LinkTag model links the models Link and Tag. It adds the Many-to-many relationship between these models. We have few useful methods like findByLink
, findByTag
to get the list of relationship. package controllers;
import play.modules.gae.*;
import com.google.appengine.api.users.*;
public class Auth {
public static boolean isLoggedIn() {
return GAE.isLoggedIn();
}
public static String getEmail() {
return GAE.getUser().getEmail();
}
public static User getUser() {
return GAE.getUser();
}
public static void login(String action) {
GAE.login(action);
}
public static void logout(String action) {
GAE.logout(action);
}
}
Code language: Java (java)
Note that above Auth
class is not part of controller (We haven’t extended it with Controller). It is just a utility class that we can use from other controllers to access user information. You may want to create your own package utils
and keep this class there. For sake of simplicity, I am keeping this in controllers
package. File: /app/controller/Application.java package controllers;
import play.mvc.*;
import models.*;
import models.User;
import java.util.*;
public class Application extends Controller {
public static void index() {
if(Auth.isLoggedIn()) {
User user = User.findByEmail(Auth.getEmail());
if(null == user) {
user = new User();
user.email = Auth.getEmail();
user.created = new Date();
user.insert();
Profile.edit(user.id);
return;
}
Profile.index(null);
}
render();
}
public static void login() {
Auth.login("Application.index");
}
public static void logout() {
Auth.logout("Application.index");
}
}
Code language: Java (java)
Check the Application controller which is the entry point to our application. The index() method will be called whenever user request homepage /. In index()
method we are checking if user is logged in. If not, then we show her a Login page using render()
method. This method renders the default view of this action. We will see this view later in our example. Also note that if user is authenticated, we redirect it to Profile.index
action. If user has logged in for first time in eazyBookmark, she will be redirected to Edit Profile page where she can select username for her profile. File: /app/controller/Profile.java package controllers;
import models.*;
import play.*;
import play.mvc.*;
import java.util.*;
import play.data.validation.*;
import java.net.*;
import net.htmlparser.jericho.*;
import java.io.*;
import play.Logger;
import models.Tag;
public class Profile extends Application {
@Before
static void checkConnected() {
if(Auth.getUser() == null) {
Application.login();
} else {
renderArgs.put("user", Auth.getEmail());
}
}
public static void index(String tag) {
User user = getUser();
List<Link> links = null;
if(null == tag || tag.equalsIgnoreCase("")) {
links = Link.findByUser(user);
} else {
links = LinkTag.findByTag(Tag.findByName(tag, user));
}
List<Tag> tags = Tag.findByUser(user);
render(user, links, tags);
}
//...
//...
}
Code language: Java (java)
The Profile controller is extended from Application controller to take advantage of login() / logout() method. Also note that this is not the full code for Profile.java. You can check the full source in the project source code. <!DOCTYPE html>
<html>
<head>
<title>#{get 'title' /}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/main.css'}">
#{get 'moreStyles' /}
<link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.ico'}">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript" charset="utf-8"></script>
#{get 'moreScripts' /}
</head>
<body>
<div id="pagewidth" >
<div id="headerbar">
<div id="headercol">
<div id="logo">
<h3><a href="/">eazyBookmark</a></h3>
</div>
<div id="headerlink">
#{if user}
${user} <span class="sep">|</span> <a href="@{Application.logout()}">Logout</a>
#{/if}
#{else}
<a href="@{Application.login()}">Login</a>
#{/else}
</div>
</div>
</div>
<div id="wrapper" class="clearfix">
<div id="maincol">
#{if flash.error}
<p class="error">${flash.error}</p>
#{/if}
#{if flash.success}
<p class="success">${flash.success}</p>
#{/if}
#{doLayout /}
</div>
</div>
<div id="footer">
<p> Copyright © ${new Date().format('yyyy')}
<a href="http://eazybookmark.appspot.com">eazyBookmark</a> </p>
</div>
</div>
</body>
</html>
Code language: HTML, XML (xml)
In above template main.html file, we have #{doLayout /}
tag which renders the content. Also note that we are displaying error / success message using flash mechanism of Play!. For each controller, we create a separate folder in views/ folder. And these folders will contain the view file for each action in given controller. Thus in /views/Application
folder we will have index.html
and in /views/Profile
folder we will have edit.html
, editlink.html
and index.html
. Download these html files from the source code attached at the end of tutorial. Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…
Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…
Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…
1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…
GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…
1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…
View Comments
To use play behind a proxy (on a unix system at least) try:
export http_proxy=http://user:pass@hostname:port
Seems to work for me.
excellent tutorial, very detailed in deed...
you should publish it on http://www.dzone.com
Play! can do the deployment to GAE in one command (precompiled, war creation, upload, etc...) by using this command :
play gae:deploy --gae={path_to_gae_sdk}
Source :
http://blog.infin-it.fr/2010/12/27/une-application-play-sur-google-appengine/
@Jean - thanks for the info. I was not aware of that command line.
Hello,
Great article!
I'm Siena committer and great fan of play! and I also develop some applications for GAE+Siena and it's really nice to see people use it as you did!
Would you accept to be referenced on Siena site as an example of site?
regards
Pascal
@Pascal - thanks for the comment. Feel free to reference the article. I am glad you liked it.
FYI, the gae module has a nice deploy command
play gae:deploy
have a look at this article (in french)
http://blog.infin-it.fr/2010/12/27/une-application-play-sur-google-appengine/
yes, deployment is even shorter if you set your $GAE_PATH environment variable.
.. then ..
play gae:deploy is enough
(it asks for your google app engine credentials the first time)
Congratulations for the article. The tutorial is excelent, it covers the important points in play, siena and gae , the source code compiles (there is a missing import for Date in models.User).
Thank you,
MVCaraiman.
Hi guys,
I tried this tutorial without creating any controller etc. I created a new play application, installed the modules updated the properties and deploy to GAE.
Should this work OK and display the default play home page? I'm getting a 500 server error.
Thanks!
There is an error in line ?
User user = getUser();
The method getUser() is undefined for the type Profile