Практически Groovy: Вперед, на сервер, с Groovy

Серверное программирование "на лету" с помощью Groovlets и GSP

Среды Groovlet и GroovyServer Pages (GSP) построены на основе Java Servlet ™ API. Однако, в отличие от Struts и JSF, серверная реализация Groovy не рассчитана на универсальное применение. Скорее это упрощённая альтернатива для быстрой и простой разработки серверных приложений. Активный пропагандист Groovy Эндрю Гловер познакомит вас с этими средами и покажет, как можно их использовать.

Эндрю Гловер, президент компании, Stelligent Incorporated

Эндрю Гловер является президентом компании Stelligent Incorporated , которая помогает другим фирмам решать проблемы качества программного обеспечения. Для этого используются эффективные стратегии тестирования и технологии непрерывной интеграции, которые позволяют коллективам разработчиков постоянно контролировать качество кода, начиная с ранних стадий разработки. Просмотрите блог Энди , там можно найти список его публикаций.



30.01.2008

Платформа Java сегодня, вероятно, стала лучшей платформой для разработки серверных приложений. Сервлеты стали прочной основой для серверных технологий Java - настолько прочной, что на основе Servlets API было построено бесчисленное множество сред – в том числе Struts, JavaServer Faces (JSF) и Tapestry. Как вы, вероятно, догадываетесь, среда Groovy также построена на основе Servlets API; однако целью этой среды является простота.

Цель Groovlet и GroovyServer Pages (GSP) состоит в том, чтобы предоставить простую и элегантную платформу для создания Web-приложений с минимальными сложностями. Так же, как GroovySql не должен быть вашим единственным средством для разработки приложений баз данных, среда Groovlet не является заменой таким функционально богатым средам, как Struts. Groovlet - это просто альтернатива для разработчиков, которым нужна простота настройки и быстрое средство создания рабочего кода.

Например, недавно мне нужно было быстро написать приложение-заглушку для проверки клиентской стороны xml-rpc-подобного API. Очевидно, что я мог достаточно быстро получить нужную функциональность с помощью сервлета, но я ни единой секунды не думал о том, чтобы обратиться к Struts. Я рассматривал возможность написания сервлета и связанной с ним логики с помощью обычного API сервлетов Java; но поскольку функциональность была нужна мне немедленно, я выбрал Groovlets.

И, как вы скоро увидите, выбор был вполне оправдан.

Прежде чем углубиться в программирование с помощью Groovlets, я хотел бы кратко повторить функции Groovy, которые понадобятся нам в коде примера. Первый раз я написал о ключевом слове def в первой статье Feeling Groovy несколько месяцев назад.

Об этой серии руководств

Чтобы применять какой-либо инструмент в практике разработки, необходимо знать, как им пользоваться и когда его лучше отложить в сторону. Языки сценариев могут быть чрезвычайно полезным помощником, но только если применять их правильно и в подходящих ситуациях. В связи с этим в серии статей Practically Groovy мы рассматриваем практические применения Groovy и показываем, когда и как его можно успешно использовать.

Определение функций в сценариях

В обычных программах Java методы должны быть определены в объекте класса. В действительности вся логика должна быть определена в контексте класса. Однако в Groovy логика может определяться внутри функций, которые могут быть определены за рамками определения класса.

Эти функции могут вызываться по названию и могут определяться в сценариях Groovy, где они сильно упрощают повторное использование кода. Для функций Groovy требуется ключевое слово def. Их можно рассматривать функции как глобальные статические методы, доступные в пределах всего сценария. Поскольку Groovy является языком с динамическим определением типов, в def не требуется объявления типов параметров, равно как не требуется и оператор return в def.

Например, в листинге 1 я определяю простую функцию, которая выводит содержание коллекции, будь то list или map. Затем я определяю list, наполнения его данными и вызываю только что определенную def. После этого я создаю map и проделываю то же самое для этой коллекции.

Листинг 1. А теперь - def!
def logCollection(coll){
  counter = 0;
  coll.each{ x | 
    println "${++counter} item: ${x}"
  }
}
lst = [12, 3, "Andy", 'c']
logCollection(lst)
mp = ["name" : "Groovy", "date" : new Date()]
logCollection(mp)

def не требует наличия оператора return, поэтому, если в последней строке формируется некоторое значение, это значение будет возвращено def. Например, код, приведенный в листинге 2, определяет def, который возвращает название класса переданной переменной. Я могу записать его без оператора return, и результат останется тем же.

Листинг 2. Оператор return в def не обязателен
def getJavaType(val){
  val.class.getName()
}
tst = "Test"
println getJavaType(tst)

Ключевое слово def может быть очень удобным для написания простых сценариев. Как вы скоро увидите, оно также будет полезно при разработке Groovlets.


Groovlets и GSP

Для работы с Groovlets и GSP требуется очень немногое: вам нужен контейнер сервлетов и последняя версия Groovy. Прелесть этих сред состоит в том, что они отображают все URL, соответствующие выбранному шаблону, на указанный сервлет посредством файла web.xml. Таким образом, первым действием в использовании Groovlets и GSP является определение контекста Web-приложения и обновление связанного с ним файла web.xml. В файле будут указываться определения классов конкретного сервлета и соответствующий шаблон URL.

Я буду использовать Apache Jakarta Tomcat, в котором я создал контекст groove. Вид директории показан в листинге 3:

Листинг 3. Листинг директории контекста groove
./groove:
drwxrwxrwx+   3 aglover  users        0 Jan 19 12:14 WEB-INF
./WEB-INF:
-rwxrwxrwx+   1 aglover  users      906 Jan 16 14:37 web.xml
drwxrwxrwx+   2 aglover  users        0 Jan 19 17:12 lib
./WEB-INF/lib:
-rwxrwxrwx+   1 aglover  users   832173 Jan 16 14:28 groovy-1.0-beta-9.jar
-rwxrwxrwx+   1 aglover  users    26337 Jan 16 14:29 asm-1.5.2.jar

В каталоге WEB-INF нужно разместить файл web.xml, в котором должны быть элементы, показанные в листинге 4:

Листинг 4. Полностью настроенный файл web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
    "http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
    <servlet>
      <servlet-name>GroovyServlet</servlet-name>
      <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>GroovyTemplate</servlet-name>
        <servlet-class>groovy.servlet.TemplateServlet</servlet-class>    
	</servlet>
    <servlet-mapping>
        <servlet-name>GroovyServlet</servlet-name>
        <url-pattern>*.groovy</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>GroovyTemplate</servlet-name>
        <url-pattern>*.gsp</url-pattern>
    </servlet-mapping>
</web-app>

Определения в приведенном выше файле web.xml указывают, что все запросы, завершающиеся .groovy (например, http://localhost:8080/groove/hello.groovy) будут направлены классу groovy.servlet.GroovyServlet, тогда как все запросы, завершающиеся .gsp, будут направлены классу groovy.servlet.TemplateServlet.

На следующем шаге необходимо разместить два файла jar в папке lib: архив дистрибутива groovy (в моём случае groovy-1.0-beta-9.jar) и соответствующий архив asm (asm-1.5.2.jar для groovy beta-9).

И всё - я готов.


Groovlet, пожалуйста

Написание Groovlet не вызывает никаких сложностей, поскольку Groovy выдвигает очень немного требований к расширению иерархии классов. При написании Groovlet не нужно расширять класс javax.servlet.http.HttpServlet, javax.servlet.GenericServlet или какой-нибудь хитрый GroovyServlet. На самом деле создавать Groovlet так же просто, как сценарий Groovy. Вам даже не нужно создавать класс. В листинге 5 я написал простой Groovlet, который делает две вещи: выводит некоторый HTML и предоставляет некоторую информацию о контейнере, в котором он работает.

Листинг 5. Первое знакомство с Groovlet
println """
<html><head>
<title>Groovlets 101</title>
</head>
<body>
<p>
Welcome to Groovlets 101. As you can see
this Groovlet is fairly simple.
</p>
<p>
This course is being run on the following servlet container: </br>
${application.getServerInfo()}
</p>
</body>
</html>
"""

Если вы просмотрите этот Groovlet в браузере, результат будет выглядеть примерно так, как показано на рисунке 1.

Рисунок 1. Результат работы простого Groovlet
Результат работы простого Groovlet

Если внимательно посмотреть на Groovlet, приведенный в листинге 5, вы вспомните время, когда только начали писать сценарии Groovy. Во-первых, в нем нет определений класса или метода main – только обычный код. Кроме того, среда Groovlet неявно предоставляет переменные экземпляра, например, ServletRequest, ServletResponse, ServletContext и HttpSession. Обратили внимание, как я обратился к экземпляру ServletContext через переменную application? Если бы я хотел перехватить экземпляр HttpSession, мне нужно было бы использовать переменную session. Таким же образом можно использовать request и response соответственно для ServletRequest и ServletResponse.


Диагностический Groovlet

Написание Groovlet не только так же просто, как и создание сценариев Groovy, в Groovlet вы также можете определять функции с помощью ключевого слова def и вызывать их напрямую. Для примера я создал Groovlet, который выполняет некоторые диагностические проверки Web-приложения.

Представьте, что вы написали Web-приложение, и его уже купили множество клиентов по всему миру. У вас есть большая клиентская база, и вы уже некоторое время выпускаете его новые версии. По прошлому опыту вы знаете, что множество звонков разъяренных клиентов связано с проблемой, возникающей в результате неправильной версии JVM и неправильного объектно-реляционного отображения (ORM).

Вы очень заняты и попросили меня найти решение. С помощью Groovlets я могу быстро создать простой диагностический сценарий, который проверяет версию VM и пытается создать сеанс Hibernate (см. раздел Ресурсы). Я начинаю с создания двух функций и вызова их при запуске сценария из браузера. Диагностический Groovlet определен в листинге 6:

Листинг 6. Диагностический Groovlet
import com.vanward.resource.hibernate.factory.DefaultHibernateSessionFactory
/**
 * Tests VM version from environment- note, even 1.5 will
 * cause an assertion error.
 */
def testVMVersion(){
  println "<h3>JVM Version Check: </h3>"
  vers = System.getProperty("java.version")
  assert vers.startsWith("1.4"): "JVM must be at least 1.4"
  println "<p>JVM version: ${vers} </p>"
}
/**
 * Attempts to create an instance of a hibernate session. If this
 * works we have a connection to a database; additionally, we 
 * have a properly configured hibernate instance.
 */
def testHibernate(){
  println "<h3>Hibernate Configuration Check: </h3>"
  try{
    sessFactory = DefaultHibernateSessionFactory.getInstance()
    session = sessFactory.getHibernateSession()
    assert session != null: "Unable to create hibernate session. 
    Session was null"
    println "<p>Hibernate configuration check was successful</p>"
  }catch(Throwable tr){
    println """
    <p>Unable to create hibernate session. Exception type is: <br/>
    <i>${tr.toString()} </i><br/>		
    </p>
    """
  }   
}
println """
<html><head>
<title>Diagnostics Check</title></head>
<body>
"""
testVMVersion()
testHibernate()
println """
</body></html>
"""

Логика проверки, реализованная в Groovlet, очень проста, но она работает. Вы просто включаете диагностический сценарий в пакет Web-приложения, и когда сотрудник службы поддержки получает звонок от клиента, он просит клиента запустить сценарий Diagnostics.groovy в браузере и сообщить результат его работы. Результат должен быть похож на приведенный на рисунке 2.

Рисунок 2. Результат работы диагностического Groovlet
Результат работы диагностического Groovlet

А как же GSP?

До этого момента я рассказывал о написании Groovlets. Однако, как вы можете видеть, страницы GSP Groovy с лёгкостью дополняют среду Groovlets, так же, как JSP дополняют Servlet API.

Внешне GSP очень похожи на JSP, но на самом деле они очень сильно различаются, поскольку среда GSP фактически является механизмом шаблонов. Если вы не знакомы с механизмами шаблонов, вам будет полезно ознакомиться со статьёй, вышедшей в прошлом месяце (EN).

Несмотря на то, что технологии GSP и JSP в корне различны, они похожи в том, что GSP отлично подходят для реализации представления Web-приложения. Как вы, вероятно, помните из статьи, вышедшей в прошлом месяце, технология, использующая представления, позволяет разделить вопросы бизнес-логики приложения и её представления. Если вы ненадолго вернётесь к диагностическому Groovlet, приведенному в листинге 6, вы можете увидеть, где будет нелишним использовать код GSP.

Этот Groovlet построен довольно криво, не так ли? Проблема в том, что в нем перемешана логика приложения и множество println для вывода HTML. К счастью, эту ситуацию можно устранить, создав простую GSP, дополняющую наш Groovlet.

Пример GSP

GSP создаются так же просто, как и Groovlet'ы. Ключ к разработке GSP состоит в понимании того, что GSP фактически является шаблоном, и, следовательно, чем меньше логики в них будет, тем лучше. Для начала я создам простую GSP, приведенную в листинге 7:

Листинг 7. Простая GSP
<html>
<head><title>index.gsp</title></head>
<body>
<b><% println "hello gsp" %></b>
<p>
<% wrd = "Groovy"
   for (i in wrd){ 
 %>
 <h1> <%=i%> <br/>
   
 <%} %>
</p>
</body>
</html>

Вид приведённой выше GSP должен напомнить вам разработку стандартных шаблонов Groovy. Синтаксис похож на JSP в плане использования <%, но, как и среда Groovlet, он позволяет использовать общие объекты сервлетов, например. ServletRequest, ServletResponse, ServletContext, а также HttpSession.


Переделайте это...

Вы можете многому научиться, реорганизуя свой старый код по мере углубления своего опыта программирования или развития платформы. Я хочу вернуться к простому приложению, формирующему отчёт, из январского выпуска, когда мы только начинали изучать GroovySql.

Как вы помните, тогда я на скорую руку слепил приложение для формирования отчётов, которое можно применять в различных местах организации. Как впоследствии выяснилось, приложение стало очень популярным для изучения активности работы с базой данных компании. Теперь сотрудники, не имеющие технических навыков, хотят получить доступ к этому важному отчёту, но у них нет желания устанавливать на свои машины Groovy для исполнения приложения.

Я, в общем, предполагал, что такое может случиться, и решение выглядит практически очевидным: Я открою доступ к приложению, формирующему отчёт, через Web. К счастью, Groovlets и GSP очень упрощают реорганизацию кода.

Реорганизация кода приложения, формирующего отчёт

Для начала я распотрошу простое приложение из листинга 12 статьи о GroovySql (EN). Реорганизовать его будет очень просто: Я просто заменю все println кодом, который передаёт экземпляр переменной в объект HttpRequest с помощью метода setAttribute().

Моим следующим действием будет перенаправление request с помощью RequestDispatcher GSP, которая будет обрабатывать компонент представления приложения, формирующего отчёт. Новый Groovlet отчёта определен в листинге 8:

Листинг 8. Исправленное приложение, формирующее отчёт по базе данных
import groovy.sql.Sql
/**
 * forwards to passed in page
 */
def forward(page, req, res){
  dis = req.getRequestDispatcher(page);
  dis.forward(req, res);
}
sql = Sql.newInstance("jdbc:mysql://yourserver.anywhere/tiger", "scott",
        "tiger", "org.gjt.mm.mysql.Driver")
   
uptime = null
questions = null
insertnum = null
selectnum = null
updatenum = null
sql.eachRow("show status"){ status |
  if(status.variable_name == "Uptime"){
         uptime =  status[1]
	 request.setAttribute("uptime", uptime)
  }else if (status.variable_name == "Questions"){
         questions =  status[1]
	 request.setAttribute("questions", questions)
  }
}
request.setAttribute("qpm", Integer.valueOf(questions) / 
Integer.valueOf(uptime) )
sql.eachRow("show status like 'Com_%'"){ status |
    if(status.variable_name == "Com_insert"){
         insertnum =  Integer.valueOf(status[1])
    }else if (status.variable_name == "Com_select"){
         selectnum =  Integer.valueOf(status[1])
    }else if (status.variable_name == "Com_update"){
          updatenum =  Integer.valueOf(status[1])
    }
}
request.setAttribute("qinsert", 100 * (insertnum / Integer.valueOf(uptime)))
request.setAttribute("qselect", 100 * (selectnum / Integer.valueOf(uptime)))
request.setAttribute("qupdate", 100 * (updatenum / Integer.valueOf(uptime)))
forward("mysqlreport.gsp", request, response)

Код, приведенный в листинге 8, должен быть вам очень знаком. Я просто заменил все println из предыдущего приложения и добавил функцию forward , обрабатывающую представление отчёта.

Добавление компонента представления

Моим следующим действием будет создание GSP, которая будет выполнять представление этого приложения. Поскольку я инженер и совсем не художник, представление будет очень простым - немного кода HTML с таблицей, как можно увидеть в листинге 9:

Листинг 9. Компонент представления отчёта
<html><head>
<title>MySql Health Report</title>
</head>
<body>
<table>
<tr>
  <td>Database Uptime:</td><td><% println
  "${request.getAttribute("uptime")}" %></td>
</tr>
<tr>
  <td>Number of Queries:</td><td><% println
  "${request.getAttribute("questions")}" %></td>
</tr>
<tr>
  <td>Queries per Minute =</td><td><% println
  "${request.getAttribute("qpm")}" %></td>
</tr>
<tr>
  <td>% Queries Inserts =</td><td><% println
  "${request.getAttribute("qinsert")}" %></td>
</tr>
<tr>
  <td>% Queries Selects =</td><td><% println
  "${request.getAttribute("qselect")}" %></td>
</tr>
<tr>
  <td>% Queries Updates =</td><td><% println
  "${request.getAttribute("qupdate")}" %></td>
</tr>
</table>
</body>
</html>

Запуск нового отчёта должен привести к результату, похожему на приведенный на рисунке 3. Цифры, конечно же, будут другими.

Рисунок 3. Результат работы исправленного приложения, формирующего отчёт
Результат работы исправленного приложения, формирующего отчёт

Заключение

Как вы можете увидеть, Groovlets и GSP являются очевидным выбором для разработки серверных приложений с простой функциональностью, которые должны быть написаны «вчера». Обе среды очень гибки и обеспечивают рекордное время перехода от кода к представлению.

Однако позвольте мне подчеркнуть, что Groovlets - это не замена Struts. Среда GSP не соревнуется напрямую с Velocity. GroovySql не является заменой Hibernate. А Groovy не является заменой языку Java.

Во всех случаях эти технологии являются вспомогательными, и в большинстве случаев Groovy является более простой альтернативой для разработки "на лету". Так же, как GroovySql - это альтернатива работе непосредственно с JDBC, Groovlets и GSP - это более удобная альтернатива работе напрямую с Servlet API.

В следующем месяце я углублюсь в удивительный мир GroovyMarkup.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=284913
ArticleTitle=Практически Groovy: Вперед, на сервер, с Groovy
publish-date=01302008