Делаем мэш-ап Twitter'а и Google Maps за 20 минут на Grails

Original author: Vladimir Grichina
  • Translation

Введение


Для многих разработчиков Java часто является синонимом ужасно занудных корпоративных приложений. Она ассоциируется с многочисленными конфигурационными файлами формата XML, шаблонным кодом и т.д. Поэтому как правило вместо нее разработчики используют динамические языки (такие как Ruby, Python, PHP) для разработки своих проектов, особенно для простых утилит, мэш-апов и т.п.

Однако в среде Java многое изменилось за последние несколько лет. Появилось много фрейморков освобождающих разрабочика от бремени корпоративных («энтерпрайзных») приложений. Grails вероятно — один из лучших. Он основан на Groovy, динамическом языке на платформе Java. Groovy создан специально для Java-программистов и переход на него максимально безболезненый. Grails используе хорошо известные, надежные и эффективные библиотеки Java (Spring, Hibernate и т.п.) для выполнения всей тяжелой работы. Существует также система плагинов и плагины для почти для всех широко используемых библиотек Java.

В этом посте я раскажу, как сделать мэш-ап из Twitter'а и Google Maps в течении всего 20 минут. Конечный результат будет выглядеть примерно следующим образом:
Geo Twitter Complete



Подготовка среды разработки


Сначала неплохо бы убедится, что установлена правильная версия JDK. Лучше всего просто скачать последнюю версию JDK здесь.
Установите ее и установите переменную среды JAVA_HOME так чтоб она указывала на каталог установки.

Затем скачайте последний релиз Grails отсюда.
Распакуйте архив в любое место, а затем установите переменную среды GRAILS_HOME чтоб она указывала на каталог в который был распакован архив. Также убедитесь что подкаталог bin/ каталога установки Grails упоминается в переменной окружения PATH.

После выполнения вышеупомянутых шагов будет возможно запускать команды Grails в консоли. Можно набрать grails help чтоб проверить это. Должен появится список доступных команд.

Основные понятия


Grails основан на шаблоне проектирования MVC. Модель представлена в domain-классах, контроллеры классами контроллеров, отображение — GSP страницами. В этом проекте используются только классы контроллеров и GSP, так как модель предоставляется Twitter API.

Не помешает прочитать краткое руководство пользователя для начала. Дополнительную информацию можно получить в руководстве пользователя. В частности, в этом посте пригодится информация о контроллерах и GSP.

Создание каркаса приложения


Одна из возможностей Grails освобождающая разработчика от «энтерпрайзного» бремени Java — возможность автоматически генерировать каркас основного приложения.
Чтобы сделать это просто наберите:

grails create-app geo_twitter

Это создаст приложение geo_twitter в текущем рабочем каталоге. Перейдите в этот каталог для всех дальнейших шагов.

Приступая к работе с Google Maps


Интеграция Google Maps проста и хорошо поддерживается Google. Однако вам сначала нужно получить ключ API, чтобы получить доступ ко всем услугам. Не бойтесь — это просто, быстро и бесплатно.

Для начала неплохо бы подчистить основной шаблон разметки (layout) — можно удалить логотип Grails и т.д. Откройте grails-app/views/layout/main.gsp и исправьте так, чтоб он выглядел следующим образом:

<html>
    <head>
        <title><g:layoutTitle default="Grails" /></title>
        <link rel="stylesheet" href="${resource(dir:'css',file:'main.css')}" />
        <link rel="shortcut icon" href="${resource(dir:'images',file:'favicon.ico')}" type="image/x-icon" />
        <g:layoutHead />
    </head>
    <body>
        <g:layoutBody />
    </body>
</html>


Потом отредактируйте grails-app/views/index.gsp чтоб интергрировать Google Maps как то так:

<html>
    <head>
        <title>Welcome to GeoTwitter!</title>
        <meta name="layout" content="main" />

        <script src="www.google.com/jsapi?key=YOUR_GOOGLE_MAPS_API_KEY" type="text/javascript"></script>

        <script type="text/javascript">
            google.load("maps", "2.x");
            google.load("jquery", "1.3.1");
        
            google.setOnLoadCallback(function() {
                $(document).ready(function() {
                    var map = new GMap2(document.getElementById('map'));
                    var vinnitsa = new GLatLng(49.2325477, 28.4744695); // Replace this by coordinates of your own city ;)
                    map.setCenter(vinnitsa, 8);
                    map.addControl(new GLargeMapControl());
                });
            });
        </script>
    </head>
    <body>
        <div id="map" style="width:800px; height:600px">
        </div>
    </body>
</html>


Конечный результат будет выглядеть как-то так:

Geo Twitter Start

Форма для Twitter'а и верстка


Добавьте простую форму с именем пользователя на страницу index.gsp.

<div class="form">
    <form action="" id="twitter">
        <p>
            <label>twitter id:</label>
            <input type="text" id="name" name="name" value=""/>
        </p>
        <p class="submit">
            <input type="submit" value="Map my friends!">
        </p>
    </form>
</div>


Затем замените основные стили в web-app/css/main.css на что-то вроде этого:

body {
    font-family: Verdana, Helvetica, sans-serif;
    margin: 1em;
}

#map {
    position: absolute;
    width: 800px;
    height: 600px;
    left: 19em;
    top: 1em;
}

.form {
    border: 1px dashed gray;
    width: 15em;
    padding: 0.5em;
}

.form label {
    width: 7em;
    display: block;
    float: left;
}

.form input {
    width: 10em;
}

.form .submit {
    padding-left: 7em;
}


Вы получите нечто подобное:
Geo Twitter Login Form


Немного особой Grails-магии серверной логики


Чтобы сделать что-то на самом деле работает, нужно добавить серверную логику. Сначала установим плагин Grails для работы с Twitter.

grails install-plugin twitter

Теперь нам нужно создать контроллер, который будет выдавать список друзей из Twitter с информацией об их местонахождении и т.д.

grails create-controller Twitter

Команда выше сгенерирует файл grails-app/controllers/TwitterController.groovy со скелетом контроллера. Его надо заменить реализацией контроллера который будет выдавать информацию о друзьях в формате JSON. Он будет также обращаться к сервису геокодирования, чтобы получать координаты на карте по имени данной местности.

import grails.converters.*

class TwitterController {
    // Google Maps API key
    static def API_KEY = "Insert your Google Maps API key here"

    // TwitterService instance will be injected into this variable by Spring
    def twitterService

    def friendsJson = {
        // Get friends of given user
        def friends = getFriends(params.name)
        // Render friends list as JSON
        render(friends as JSON)
    }

    private def getFriends(String userName) {
        def friends = twitterService.getFriends(params.name)
        
        // Return only the needed fields for each user and retrieve coordinates for location
        friends.collect { it ->
            [
                screenName: it.screenName,
                name: it.name,
                pictureUrl: it.profileImageUrl as String,
                bio: it.description,
                status: it.status?.text,
                coords: getCoordsFromLocation(it.location)
            ]    
        }
    }
    
    /**
     * This method gets coordinates on map for given location string.
     */

    private def getCoordsFromLocation(String location) {
        if (location) {
            if (location.contains("iPhone:")) {
                // There can be coords specified in location
                // like iPhone: 39.035248,-77.138687
                location = location.replace("iPhone: ", "")
                def parts = location.split(",")
                return [latitude: parts[0], longitude: parts[1]]
            } else {
                // Encode location as URL
                def encodedLocation = URLEncoder.encode(location)
                // Call web service by retrieving URL content
                def response = 
                    "maps.google.com/maps/geo?q=${encodedLocation}&output=xml&key=${API_KEY}".toURL().getText()
                // Parse response XML
                def root = new XmlSlurper().parseText(response)
                if (root.Response.Placemark.size() == 1) {
                    def coords = root.Response.Placemark.Point.coordinates.text()
                    def parts = coords.split(",")
                    if (parts.size() > 1) {
                        return [latitude: parts[1] as Double, longitude: parts[0] as Double]
                    }
                }
            }
        }

        // No coordinates are determined
        return null
    }
}


Использование AJAX для получения данных от сервера


После того как мы написали логику контроллера, нужно написать JS код, который будет получать данные с сервера и отображать их на карте. Этот код можно разместить в обработчике отправки формы, но сначала надо задать правильное действие для формы:
action="${createLink(controller: 'twitter', action: 'friendsJson'}"

Потом добавим обработчик в index.gsp, наподобие:

google.load("maps""2.x");
google.load("jquery""1.3.1");

google.setOnLoadCallback(function() {
    $(document).ready(function() {
        // Create and configure Google Map control
        var map = new GMap2(document.getElementById("map"));
        var vinnitsa = new GLatLng(49.232547728.4744695);
        map.setCenter(vinnitsa, 4);
        map.addControl(new GLargeMapControl());
        // Add form submit handler
        var form = $("#twitter");
        form.submit(function() {
            $.getJSON(form.attr("action"+ "?" + form.serialize(), function (data) {
                // Clear all markers
                map.clearOverlays();
                // Loop through friends list and add markers to map
                $.each(data, function (i, item) {
                    if (item.coords) {
                        var marker = new GMarker(new GLatLng(item.coords.latitude, item.coords.longitude));
                        map.addOverlay(marker);
                        var popup = '<img style="width: 48px; height:48px;" src="' + item.pictureUrl + '">' + 
                            item.name + ' (' + item.screenName + ') <br>' + 
                            item.bio + '<br>' + item.status;
                        GEvent.addListener(marker, "click"function() {
                            marker.openInfoWindowHtml(popup);
                        });
                    }
                });
            });
            // Indicate that form should not actually be submitted
            return false;
        });
    });
});


Теперь, когда вы введете имя и нажмете кнопку «Map my friends!» получите следующую картину:

Geo Twitter Complete

Демо и исходники


  • Исходники демо-проекта можно взять в репозитории GitHub
  • Демо можно посмотреть на нашем демо-сервере (смотрите ссылку в блоге на английском), если он не упадет, а он таки упадет :)


Полезно почитать еще


Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 44

    +2
    Отличное введение в Groovy on Grails!
      0
      Боюсь как введение данная статья всё-таки плохо подходит. Это скорее teaser. ;)
      Но документация у Grails очень неплохая.
        0
        Документация неплохая, но проблемы в реальной жизни бывают интересные, особенно с плагинами ) К тому же к плагинам документация часто не супер.

        Я планирую писать про решение разных проблем на блоге, только вот наверное переводить не буду, так как все-таки слишком нишевая тематика для Хабра.
          0
          Это да, самому приходилось ковырять-исправлять некоторые плагины. :) Сейчас играюсь со связкой Grails & GWT.
      +4
      Гм реально Груувиии :)
      Но нет, джава в меня не влезет
        0
        Осмелюсь предположить непреодолимую зависимость от Python. :)
      • UFO just landed and posted this here
          0
          Ммм. Safari под вистой выглядит точно также :-)
            0
            а можно дурацкий вопрос. как вы делаете такие красивые тени, неужто руками в фотошопе?
              0
              Скриншоты окон через command-shift-4-пробел в Мак ОС делаются сразу с тенями и на прозрачном фоне :)
                +6
                нинавижу!!11 :-)
                • UFO just landed and posted this here
              • UFO just landed and posted this here
                  0
                  нет
                    0
                    Да. Только кнопочки справа.
                    0
                    Это уже тенденция… Все скрины в OS X…
                      0
                      А еще на скринах локализованная версия системы…
                        0
                        Не, iMac, изначально приобретенный для разработки под iPhone, но затягивает зараза )
                          0
                          многие java-девелоперы исполльзуют маки, кстати
                          0
                          спасибо за перевод. Давно хотел попробовать…
                            0
                            Очень вовремя, спасибо! Как раз на работе появилась необходимость в groovy & grails.
                              0
                              За Винницу плюс в карму :)

                              Спасибо, было интересно. А можно с примерами, но уже не-мешаповских приложений? Тема действитльно очень актуальная.
                                0
                                Ну думаю как что-то еще по теме буду писать в блог — переведу тоже.
                                  0
                                  а примеры каких приложений вас интересуют?
                                  есть, например, написание своего твиттера за 40 минут
                                  0
                                  О, а я собрался уже в оригинале читать. )
                                  Спасибо!
                                    0
                                    Подписывайтесь на наш блог, постараюсь еще интересное что-то писать, не все смогу переводить.
                                    0
                                    может, в блог groovy&grails?

                                    зы спасибо за интересный пост
                                      0
                                      Мне кажется пост все-таки больше для тех кто не пишет на Groovy/Grails
                                        0
                                        Это пост-замануха. ;) К сожалению Groovy&Grails пользуются не очень большим спросом. Надеюсь пока.
                                          0
                                          Там всего 4 новости. Я все свои публикую в Java — аудитория блога куда больше, да и темы часто перекрёстные всё же.
                                            +2
                                            вот и собрались все g/g-девелоперы хабра в коментах к одному коменту :)
                                              0
                                              Умпутун ещё на Groovy что-то пишет. Но особо не пропагандирует.
                                            0
                                            Наконец то появилось что то про Groovy/Grails, наверное стоило рассказать про доменные классы, про GORM, ну думаю все это будет, буду ждать еще постов.
                                            Интересна тема реализации «дружбы» GWT и Grails.
                                              0
                                              Попробовал и у меня не работает.
                                              А как у вас index.gsp знает, что надо метод friendsJson() вызывать?
                                                0
                                                Блин, напортачил.
                                                Не увидел одну строку. Извините за панику.
                                                  0
                                                  А вы код из статьи или из github используете? В github поновее и получше :)
                                                  0
                                                  В коде ошибка, пропустили скобку при регистрации ссылки:
                                                  action="${createLink(controller: 'twitter', action: 'friendsJson')}"
                                                  
                                                    0
                                                    Не понял что вы имеете в виду.
                                                      0
                                                      Пропущена последняя скобка:
                                                      action="${createLink(controller: 'twitter', action: 'friendsJson')}"
                                                  0
                                                  И кстати, для простого демо приложения, которое никуда ставиться не будет, API key не нужен.
                                                    0
                                                    Насколько я знаю для API геокодирования нужен, в отличии от самих карт.
                                                      0
                                                      API key нужен. Тут снова ошибся.

                                                      Пришлось добавить http:// в строку
                                                                      def response =
                                                                          "http://maps.google.com/maps/geo?q=${encodedLocation}&output=xml&key=${API_KEY}".toURL().getText()
                                                      

                                                      Иначе выпадала ошибка «java.net.MalformedURLException: no protocol».

                                                      Но пример все равно у меня не заработал. Время от времени падает с такой ошибкой:
                                                      Caused by: winterwell.jtwitter.TwitterException: Errror re http://twitter.com/statuses/friends/igor_shubovych.json:
                                                      null
                                                      	at winterwell.jtwitter.Twitter.fetchWebPage(Twitter.java:738)
                                                      	at winterwell.jtwitter.Twitter.getFriends(Twitter.java:882)
                                                      	at com.burtbeckwith.grails.twitter.service.TwitterService.getFriends(TwitterService.groovy:181)
                                                      	at com.burtbeckwith.grails.twitter.service.TwitterService$getFriends.call(Unknown Source)
                                                      	at TwitterController.getFriends(TwitterController.groovy:24)
                                                      

                                                      А иногда вот такое
                                                      org.codehaus.groovy.runtime.InvokerInvocationException: java.io.IOException: Server returned HTTP response code: 403 for URL: http://maps.google.com/maps/geo?q=Ukraine&output=xml&key=ABQIAAAAoboZS-nzwx1YaGkU-ISITBTvIJmRro598FiaoBiKal8Wie_6IBQhNA86khwZeQ9s3kkLB8B7MFI4qA
                                                      
                                                        0
                                                        наконец-то заработало. ничего не менял. :(
                                                          0
                                                          Хабрапарсер съел http:// . Вообще рекомендую взять код с github. А так то ошибки бывают хотя бы потому что API твиттера не очень стабильный.

                                                      Only users with full accounts can post comments. Log in, please.