Меня давно вопрос — на чём проще всего сделать веб приложение ява программисту неискушенному в веб разработке.
Меня смущали несколько вещей — во первых — регистрация пользователей, логин, права доступа и так далее. Не хотелось с этим много возиться. Решение здесь я нашел в Grails с плагином Spring Security.
Во вторых —
И вот, наконец я нашел решение. Это — Gaelyk. Groovy фреймворк для Google App Engine.
Чтобы понять что он даёт и как его использовать давайте посмотрим на простейший проект.
Простейший проект можно сделать скачав template project или воспользовавшись maven archetype
Однако же первый из них использует gradle, а не maven и оба используют стандартный groovy компилятор, а не экслипсовский, который позволяет не создавать стабы, и оба не создают пример работы с Google datastore
Поэтому я создал свой maven archetype. Давайте с его помощью и создадим Gaelyk проект:
mvn archetype:generate -DarchetypeGroupId=org.bernshtam -DarchetypeArtifactId=gaelyk-archetype -DarchetypeRepository=http://bernshtam.name/maven2 -DgroupId=myexample -DartifactId=test1 -DarchetypeVersion=1.0
Теперь можно сделать импорт созданному проекту в IDEA (в принципе и сгенерировать проект из архетайпа можно в IDEA тоже):
После импорта остается добавить Facet Google App Engine:
После этого добавляем конфигурацию запуска.
Обратите внимание что Google App Engine development kit, должен быть уже инсталлирован у Вас на машине и указан в IDEA (я указал кнопку). Все остальные зависимости принесет maven.
Нажимаем на мавеновский install:
И запускаем!
Ура, работает! Это простейшее приложения для создания заметок с приоритетом (зачем заметкам приоритет — я не знаю :) )
Теперь давайте посмотрим, что же там есть.
web.xml ставит сервлет для грувлетов и шаблонов (gtpl)
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<listener>
<listener-class>groovyx.gaelyk.GaelykServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>GroovletServlet</servlet-name>
<servlet-class>groovyx.gaelyk.GaelykServlet</servlet-class>
<init-param>
<param-name>verbose</param-name>
<!-- Set it to true for more details -->
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>TemplateServlet</servlet-name>
<servlet-class>groovyx.gaelyk.GaelykTemplateServlet</servlet-class>
<init-param>
<!-- Remove the default "generated by" messages from the templates -->
<param-name>generated.by</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>verbose</param-name>
<!-- Output generation time in the HTML, see source page -->
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<filter>
<filter-name>RoutesFilter</filter-name>
<filter-class>groovyx.gaelyk.routes.RoutesFilter</filter-class>
</filter>
<servlet-mapping>
<servlet-name>GroovletServlet</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>TemplateServlet</servlet-name>
<url-pattern>*.gtpl</url-pattern>
</servlet-mapping>
<filter-mapping>
<filter-name>RoutesFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.gtpl</welcome-file>
</welcome-file-list>
</web-app>
appengine-web.xml дескриптор для Google App Engine. Вам надо заменить test1-archetype на имя своего приложения app engine, предварительно созданного в консоли app engine
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0-SNAPSHOT">
<application>test1-archetype</application>
<version>1</version>
<!-- Enable concurrent requests by default to serve requests in parallel -->
<threadsafe>true</threadsafe>
<!-- If all your templates and groovlets are encoding in UTF-8 -->
<!-- Please specify the settings below, otherwise weird characters may appear in your templates -->
<system-properties>
<property name="file.encoding" value="UTF-8"/>
<property name="groovy.source.encoding" value="UTF-8"/>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
</system-properties>
<!-- Uncomment this section if you want your application to be able to receive XMPP messages -->
<!-- And create a file named jabber.groovy in /WEB-INF/groovy -->
<!-- Similarily, if you want to receive incoming emails -->
<!-- And create a file named email.groovy in /WEB-INF/groovy -->
<!--
<inbound-services>
<service>xmpp_message</service>
<service>mail</service>
</inbound-services>
-->
<!-- It it advised to keep this parameter to true -->
<!-- as per the release of SDK 1.2.8, -->
<!-- this should speed up cold startups of applications by 10% -->
<precompilation-enabled>true</precompilation-enabled>
<static-files>
<exclude path="/WEB-INF/**.groovy" />
<exclude path="**.gtpl" />
</static-files>
</appengine-web-app>
Теперь специфика Gaelyk. routes.groovy содержит инструкции какой запрос куда перенаправлять
get "/", redirect: "listnotes.groovy"
get "/favicon.ico", redirect: "/images/favicon.png"
Gaelyk это MVC фреймворк, а значит чаще всего запросы будут перенаправляться на контроллеры, в роли которых тут выступают грувлеты. Полное описание DSL для routes.groovy можно посмотреть здесь.
Теперь посмотрим на класс Note:
package myexample
import groovyx.gaelyk.datastore.Entity
import groovyx.gaelyk.datastore.Key
import groovyx.gaelyk.datastore.Unindexed
@Entity(unindexed=false)
class Note {
@Key long id
String login
@Unindexed int priority
String text
}
По умолчанию Gaelyk предлагает простейшую интеграцию с Google App Engine datastore — это не полноценный ORM, не JPA, но для простейших вещей подходит.
Можно посмотреть примеры в контроллерах:
Note note = new Note(priority:priority, text: text, login: email)
note.save()
...
def note = Note.get(id)
note.delete()
..
def notes = Note.findAll { login == email }
Элегантно, не правда ли?
Теперь посмотрите на это:
def email = user.email
...
log.fine("$notes ${notes.size()}")
...
String text = request.getParameter("text")
Переменные user, request и log не определены. Они инжектятся, как и многие другие. В частности user будет null, если пользователь не сделал логин и будет содержать класс содержащий детали пользователя если сделал. log содержит логгер.
request — это HttpRequest
Вообще, вы будете удивлены сколько всего автоматически инжектится в Ваши контроллеры и шаблоны (а по желанию — и в любые классы)
Модель из контроллеров в шаблоны передается через свойства request:
request['notes'] = notes
forward "index.gtpl"
Осталось посмотреть на шаблоны. Они похожи на jsp, содержат % include %, вставки кода
<% include '/WEB-INF/includes/header.gtpl' %>
<h1>My notes</h1>
<% if (user) { %>
<p>
<table width="50%" border="1">
<tr><th width="30%">Note</th><th>Priority</th><th></th></tr>
<% request.notes.each { note -> %>
<tr><td>${note.text}</td><td align="left"> ${note.priority}</td><td><A href="deletenote.groovy?id=${note.id}">X</A> </td></tr>
<% } %>
</table>
</p>
<div class="add">
<h2>Add a new note</h2>
<p>
<form name="addnote" action="addnote.groovy" method="post">
Priority: <input name="priority" id="priority" type="number" value="1" min="1" max="10"/><br/> <br/>
Text: <input name="text" id="text" type="text"/>
<input type="submit" value=" Add Note "/></form>
</p>
</div>
<% } else { %>
<p><A href="${users.createLoginURL("/")}">Login</A> </p> to access your notes
<% } %>
<% include '/WEB-INF/includes/footer.gtpl' %>
Заметьте проверку на то, что пользователь залогинился ( if (user)
), вытаскивание модели из request ( request.notes.each
), использование груви стиля ${note.priority}
, создания ссылки на логин: ${users.createLoginURL("/")}
(users — еще одна инжектнутая переменная).
Страница с логином в разработке выглядит совсем не так, как после деплоймента на Google App Engine, она дает возможность логинится под разными адресами, как админ и не как админ
Если понравилось, начинайте читать tutorial
Автор: javax