Groovy + JSON + Prototype in Action

Рубрика: Development, Groovy, Java, JavaScript/Ajax, MySQL | 6 April 2007, 18:53 | Vadim Voituk

Дабы у читателя не возникало мысли, что Groovy подходит только для написания простеньких утилит и prebuild-скриптов предлагаю рассмотреть пример создания серверного приложения на Groovy с использованием коктейля Web2.0-ппопулярных технологий.


Предположим что нужно разработать сервис для получения информации о пользователе из некой базы данных.
Причем информацию необходмо получать в формате JSON – как-никак Web 2.0 на дворе.

План действий:

  1. Описываем необходимые требования
  2. Создаем базу данных и заполняем её тестовыми данными
  3. Создаем пул соединений к базе данных
  4. Описываем сущность User
  5. Создаем сервлет “Информация о пользователе” (ответ формате JSON)
  6. Создаем сервлет “Список пользователей”
  7. Создаем “клиентскую” часть, получающую данные от сервлета “Информация о пользователе”

Итак, для разработки нам необходимо:

Перед началом необходимо скопировать файлы groovy-all-1.0.jar и mysql-connector-java-5.0.4-bin.jar в директорию $TOMCAT_HOME/common/lib/.
По идее дожно быть достаточно положить его в WEB-INF/lib директорию своего web-приложения, но в таком случае у меня он не заработал, а желания искать причину у меня небыло.

Также копируем в WEB-INF/lib приложения commons-beanutils-core.jar, ezmorph-1.0.1.jar и json-lib-1.1-jdk15.jar

Создаем базу данных:
[sql] CREATE DATABASE jwap;[/sql]

и таблицу для хранения пользователей:
[sql]

 CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` char(255) NOT NULL default '',
  `email` char(50) NOT NULL default '',
  `b_date` date NOT NULL default '0000-00-00',
  `icq` bigint(20) unsigned NOT NULL default '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `u_name` (`name`)
) ENGINE=MyISAM

[/sql]
добавляем тестовые данные:
[sql]INSERT INTO users (name, email, b_date,icq) VALUES
(‘Vadim Voituk’, ‘vadim@microsoft.com’, ’1980-11-22′, 944328),
(‘Bill Gates’, ‘bill@microsoft.com’, ’1954-01-04′, 123456789)[/sql]

Создаем пул соединений к базе данных
Назовем его jdbc/MYSQLDB.
Для этого в META-INF/context.xml пишем следующее:
[xml]<?xml version=’1.0′ encoding=’utf-8′?>
<Context>
<Resource
name=”jdbc/MYSQLDB”
auth=”Container”
type=”javax.sql.DataSource”
username=”mysql_user”
password=”my-mysql-password”
driverClassName=”org.gjt.mm.mysql.Driver”
url=”jdbc:mysql://localhost/jwap”
validationQuery=”SELECT 1″
maxActive=”150″
maxIdle=”15″
/>
</Context>[/xml]
Создавать пул соединений не обязательно, но мне кажется что так будет удобнее.

Описываем сущность User:
[java]

class User {
  int id;
  String name;
  String email;
  int icq;
}

[/java]
На этом этапе хочется заметить что создавать сервлеты с помощью Groovy можно двумя способами: создавая подкласс javax.servlet.http.HttpServlet и с помощью GroovyServlet. Здесь мы рассмотрим оба варианта.

Создаем сервлет “Информация о пользователе”
[java]

import javax.servlet.http.*;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import groovy.sql.Sql;
import net.sf.json.JSONSerializer;

class UserInfo extends HttpServlet {

  /**
  * @see
  */
  public void service(HttpServletRequest request, HttpServletResponse response) {

    String id = request.getPathInfo();
    if (!id?.startsWith("/") || id?.length()<=1) return;

    id = id[1..id.length()-1];

    def numId;
    try { numId = id.toInteger(); } catch (NumberFormatException e) { return; }

    PrintWriter out = response.getWriter();

    InitialContext initCont = new InitialContext();
    DataSource dataSource = (DataSource)initCont.lookup("java:comp/env/jdbc/MYSQLDB");
    def db = Sql.newInstance(dataSource);

    try {
      db.eachRow("SELECT * FROM users WHERE id='${numId}'") {
        def user = new User(id:it.id, name:it.name, email:it.email, icq:it.icq);
        out.println JSONSerializer.toJSON(user,  ["metaClass"] as String[])
      }
    } catch (Exception e) {
      out.println("Error: " + e.message);
    } finally {
      db.close();
      out.close();
    }
  }

}

[/java]
в web.xml дописываем:

[xml]

<servlet>
<servlet-description>Return user info</servlet-description>
<servlet-name>UserInfo</servlet-name>
<servlet-class>com.point.heartbeat.UserInfo</servlet-class>
</servlet>

<servlet-maping>
<servlet-name>UserInfo</servlet-name>
<url-pattern>/user/*</url-pattern>
</servlet-maping>
[/xml]
переходим по адресу http://myserver.com:8080/groovy/user/1 и вуаля!:
{"email":"vadim@microsoft.com","name":"Vadim Voituk","id":1,"icq":944328}
Мы получили JSON представление обьекта User с id=1.

Тут был показан классический метод создания сервлетов, применимый в Java.

Теперь на примере сервлета “Список пользователей” покажу как легко создавать сервлеты используя для этого возможности Groovy.

Для этого дописываем в web.xml такие строки:
[xml]
<servlet>
<servlet-description>Groovy servlet</servlet-description>
<servlet-name>GroovyServlet</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>GroovyServlet</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>

[/xml]
Далее создаем в WebContent-директории (!!!) скрипт:
[java]

import javax.naming.InitialContext;
import javax.sql.DataSource;
import groovy.sql.Sql;

response.setContentType("text/html;charset=utf-8");

InitialContext initCont = new InitialContext();
DataSource dataSource = (DataSource)initCont.lookup("java:comp/env/jdbc/MYSQLDB");
def db = Sql.newInstance(dataSource);

try {
  db.eachRow("SELECT * FROM users ORDER BY id") {
    out.println "

$it.name

"
  }

  out.println '''
.user_item {Cursor: pointer;}




''';

} catch (Exception e) {
  out.println("Error: " + e.message);
} finally {
  db.close();
  out.close();
}

[/java]

Теперь переходим по адресу http://mserver.com:8080/groovy/UsersList.groovy и видим список пользователей

И напоследок добавим возможность при клике на имя пользователя смотреть подробную информацию о нем.
Тут нам понадобится JavaScript библиотека Prototype. Можно конечно написать все на чистом JavaScript но после 4-х месяцев использования Prototype я стал ленивым и предпочитаю не изобретать велосипед.

Всю логику на JavaScript я перенес в файл my.js.

А результат всех мытарств выглядит вот так невзрачно:
groovy-json-example.PNG

Полные исходные коды примеров.

Вышло все немного сумбурно, но я готов отвечать на вопросы и вносить коррективы :)

Комментариев: 12

12 Responses to “Groovy + JSON + Prototype in Action”

Комментарии:

  1. Скакунов Александр

    Ты знаешь ДР Билла Гейтса!

  2. vadim

    Нет, не знаю. Просто ввел случайную дату.
    На самом деле Бил Гейтс родился 25 октября 1955 г.

  3. schleicher

    Случайно не задавался вопросом об UTF-8 в Groovy сервлете?
    Я просто кучу интернета уже извел, а воз и ныне там… Проблема в том, что исходный текст .groovy файла в UTF8 подвергается доп. перекодировке при выводе…

  4. vadim

    schleicher: Очень даже задавался :) У меня была та же проблема.
    Для по умолчанию response output stream идет в кодировке отличной от UTF-8 (сразу и не вспомню какой именно), пототому его нужно перевести с UTF-8.
    Делается это приблизительно так:
    [java]OutputStream os = response.getOutputStream();
    OutputStreamWriter osw = new OutputStreamWriter(os , “UTF-8″);
    PrintWriter o = new PrintWriter(osw);
    [/java]
    и далее везде вместо println “Hello world!” писать o.println “Hello world!”.

    Как это работает можно посмотреть в исходных кодах примера.

  5. schleicher

    В кодировке дефолтной системной. В этой кодировке груви считывает файл, отчего собсно и все проблемы. При запуске или компиляции из консоли вроде как можно указывать кодировку, а вот в сервлете непонятно как это сделать…
    я так понял, что устанавливая response.content-type мы перекодируем из системной кодировки в ту, что указали.
    то есть корень зла – в считывании файла скрипта!
    Я, балуясь с Jruby сервлетами, ходил по тем же граблям, пока не переписал org.jruby.RubyServlet считывание файла скрипта. А в Groovy это спрятано далеко в недра jar’a… вчера пытался добраться да спать сильно хотелось :-)

    Твой способ тоже не совсем корректен. Данные из MySQL ты принимаешь в cp1251. А потом они автоконвертируются в UTF-8. А попробуй базу в UTF! А попробуй просто
    o.print(“труляля”)
    у меня во всяком случае выводит порушенный конвертацией текст.

    Можешь привести ссылку на этот вопрос в groovy-user? и вообще, где этот groovy-user, может есть смысл читать? :-)

  6. vadim

    В принципе без разницы в какой кодировке у меня данные – на выходе из JDBC-дравера я их получаю в юникоде.
    А вот насчет системной кодировке – тут ты прав.
    Провел несколько тестов и убедился.

    А читать рассылки groovy-user можно тут
    http://www.nabble.com/codehaus—Groovy-f11866.html
    Там же можно и задавать вопросы.
    Если удасться что-то узнать по этому поводу – буду благодарен если сообщишь.

  7. giridhar

    awesome article.

    where can i get the english version

  8. vadim

    Why awesome?
    There is no English version avaible.
    You can ask your questions here.

  9. schleicher

    В общем так. Все что касаемо UTF-8 решилось только написнием своего сервлета. Там же мне было нжуно маппить все запросы на один скрипт, чего нет в стандартном GroovyServlet. Буду рад, если проявишь внимание к моей статье:

    http://schleicher.ru/blog/240.html

  10. Alno

    А что случилось с кодом? У меня почему-то он весь в одну строку.
    Браузер: Firefox 3

  11. Vadim Voituk

    Alno: вроде исправлено :)

Leave a Reply