Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!


Интересна Java? Кликай по ссылке и изучай!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 
Показаны сообщения с ярлыком Web. Показать все сообщения
Показаны сообщения с ярлыком Web. Показать все сообщения

понедельник, 10 июня 2024 г.

Приложения - конструкторы

А что если бизнес в наше время ускорился до таких скоростей, что классический способ построения приложений уже не о правдывает себя. Мы возложили на команду разработки беклог, и ждем когда они его реализуют. Что если разработчики приложения должны подумать о том, чтобы создать конструктор на базе своего приложения, и дать возможность пользователям кастомизировать свой UI/UX по их потребности. Пусть будет базовый интерфейс приложения by-default используемый неподготовленным юзером. Но для подготовленного юзера всегда будет возможность залезть под капот и настроить любой скрин этого приложения кастомно. Да это будет более сложное взаимодействие, чем мы привыкли в Web 2.0 и скорее всего придется выучить тот или иной скриптовый или декларативный язык конфигурации этого приложеняи конструктора. Пользователю не надо ждать, когда команда разработки закончит свой беклог, им уже создали конструктор - надо лишь только разобраться как что устроено и сделать свою кастомизацию. 

Я все свои pet проекты пишу теперь такими конструкторами. Всегда есть некоторый скрипт/конфиг, который позволяет без перекомпиляции приложение поменять его свойство. Даже в этом блоге есть зародыши такой возможности - могу использовать режим верстки, или погрузиться в увлекательный мир HTML и сверстать все по-инженерному. Уверен множество приложений имеют такой advanced тулинг стоит присмотреться. Я лишь рекомендую довести это до максимума и внедрить новый подход в разработку ПО. И дело не только в обработке данных, которые передаю на вход из фронтенда в бекенд. Можно пойти дальше и дать возможность влиять на бизнесс логику.
 
Может показаться, что это не безопасно. Открывая больше дверей мы делаем более уязвимыми наши сервера. Но сейчас могу так же открыть Inspect вкладку браузера и на любой страничке посмотреть множество запросов и выполнить большую часть из их независимо от других. Могу написать свой UI поверх существующего API. Часто разработчики сами открывают свои API и документацию на него. Но это не совсем то, что предлагаю. Я хочу иметь возможность влиять на то как будет работать мое приложение путем задания сценария бизнесс логики.

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

 
Так кажется, стоит мне отредактировать любой блок, как я тут же увижу html/js/css код.


То есть если я лезу в настройки приложения, я вероятнее всего должен быть инженером.
 
Если посмотреть в другие настройки - я могу так же изменить HTML в который все будет собираться в результате. 
 

Это то о чем я говорю. Но опять же очень глубоко спрятано и сделано достаточно сложно. Сложно в обе стороны. Для разработчиков скринов настройки блога это еще один скрин, который стоит времени разроаботчика - взять тот же drag & drop конкретных блоков. Надо ли это мне инженеру? Ну мне было бы проше отредактировать текстовый файл конфигурации. Надо ли это юзеру без навыков кодирования? Скорее всего он сюда не доберется, а добравшись будет делать что-то что ему расскажут в видео мануале или посте в блоге, который он нагуглит - "...хотите добавить кнопочку с рассылкой, скопируйте этот текст и вставьте его сюда...". Мне же этот скрин не позволяет сделать чуть больше, чем позволено. Там есть зашита общая структура блога: сверху navbar, ниже crosscol, потом идут основной main и справа от него sidebar. Я уверен что и этот темплейт где-то зашит в коде. Я бы хотел иметь возможность редактировать его. 

Но если мы пойдем дальше. Вот у меня есть список постов в блоге. И я хочу его видеть в другом виде.Могу ли я сейчас повлиять на это? Нет


Пойдем дальше. Покусимся на бизнеслогику. После публикации поста он появляется в блоге, прием можно сделать отложенную публикацию. А что если я хочу тут добавить дополнительный флоу - скажем написать email или вызвать какой-то rest api чтобы сообщить о факте публикации? А что если я хочу иметь возможность так же делать какие-то полезные мне действия при каждом открытии поста читателем? Что если я хочу рендерить пост по разному, в соответствии с тем какое время суток? Что если я хочу не в html формате хранить мой пост, а в markdown? Что если я хочу хранить свои посты не в базе blogger, а на github? Могу ли я сейчас на это все влиять? Нет конечно. Потому я отправляюсь в opensource мир и ищу блог, который мне больше всего подойдет. А если не найду оного - сажусь писать свой конструктор. 

Самая лучшая метафора того, как мне кажется будет уходить web в ближайшие годы - конструктор лего. Есть книжечка, где описано как построить домик с озером. Есть другая книжечка, в которой написано как построить космический корабль. И есть большой мешок с кубиками разных размеров. Пользуясь этой метафорой я предлагаю пользователю все в собранном виде вместе с этими книжечками, что дает возможность ему пользоваться функционалом сразу. Но если в будущем пользователь захочет пристроить лазерную пушку снятую с косчического корабля на крышу своего загородного домика с озером - у него есть такая возможность.

На сегодня все мои pet приложения так или иначе реализуют этот подход. И я могу сказать что это достаточно сильно меняет и подход в разработке и архитектуру. Когда вы делаете конструктор - все должно быть разбираемо и собираемо обратно, причем в другой форме. Детализация компонентов должна быть проработана в том месте, где требуется гибкость. Главная идея, чтобы при пересборке приложение все еще оставалось в изначальном домене: если это изначально был blog, то после переконфигурирования это будет все так же blog, а не какая-то скажем crm. Решить, где добавлять конфиг, что бить на компоненты, насколько мелко декомпозировать, а что оставить проприетарным монолитом - хорошие вопросы.

Сейчас например я работаю над микросервисом по построению LLM цепочек для решения своих несложных задач автоматизации рутины с помощью LLM. Все эти цепочки вынесены в конфиг, который является кастомизацией yaml формата. Я слежу за тем, чтобы конфигурирование цепочки оставалось простым. Но все же без инженера в них не разобраться. Потому есть многочисленные демо решения - файлики с уже созданными цепочками, в которых делается что-то полезное, что может быть относительно быстро исправлено не инженером и реализовано под свои нужды.

Другой мой пет проект - CRM система ведения кандидата по воронке стафинга и онбординга на проект. Сама воронка состоит из шагов, каждый из которых описан в markdown и превращается в html страничку которую видит тот или иной коллега - кандидат, его менеджер, рекрутер, HR специалист и так далее. Все они делают на страничке что-то с данными, завязанными на этого конкретного кандидата - выступают consumer или supplier этих данных. Некоторые шаги могут быть пропущены в заивсимости от определенных conditions. Так как на разных проектах последовательность шагов может быть иной - я как delivery менеджер этого проекта настраиваю эту цепочку сам. На выходе я получаю отдельный проект с набором markdown страничек где описан весь флоу (что полезно для KT другому менеджеру кто прижет на мое место), я могу добавлять улучшения в этот флоу и видеть в git историю изменений. Будучи загружена в приложегние эта цепочка превращается в конкретные скрины, странички, поля ввода/вывода, формы, таблички, реакции на сторонние сервисы как то отправка email и так далее. Для этого мне пришлось добавить свои новые теги к существующим в markdown. Конечно же пиать этот скрипт воронки придется тому, кто по-инженгерному разберется в нем. Но я точно знаю, что delivery manager IT компании точно сможет найти в своей команде инженера-волонтера, кто поможет ему в этом разобраться в свободное от работы время.

Еще один pet проект - блог. Я хочу переехать с blogspot и писать посты в markdown. Я хочу иметь возможность концигурировать свой блог подобно тому, как описывал это в этом посте выше. Скажем я хочу иметь возможность разместить 1 линку на youtube видео, и настроить в конфиге фильтр, который в зависимости от ссылки на видео будет вставлять либо youtube embed html блок, либо подобный блок другого видео хостинга. Причем я хочу описать это один раз и в одгом месте, а не копипастить постоянно этот код в каждый мой пост. Я хочу сохранять мои посты в git репозитории с версиированием, а блог система пускай поднимает его из git repo по моему запросу. 

Другой мой проект Codenjoy хоть и не конфигурируется в скриптовом формате, но все же c самого начала позволяет создавать новые игрушки очень быстро. Все сделано для того, чтобы сегодня (максимум завтра) вечером появилась новая игра, если ты того пожелаешь. Да - тебе придется кодить на java. Но все API построено таким образом, чтобы это было максимально просто.

Есть и другие, о них я расскажу в другой раз.

среда, 23 апреля 2014 г.

Как я кодю - проект PhotoSlider [Web/Java/Maven/Jetty/Spring/WebDriver]

В прошлом много всяких кодинг-отчетов писал. Пользовался при этом скриншотами и текстом. Но хочется пойти дальше. Было несколько попыток снимать видео того, что происходит на экране пока я кодю - судя по отзывам весьма успешные.

Ценность какая в этом всем? Я бы с удовольствием по-подглядывал как кто-то делает свою работу. Уверен, что есть ребята, которым это так же интересно. А так как правило "будь источником изменений, которые хочешь увидеть" работает, то начну (или продолжу) с себя.

Я люблю много фоткать, особенно, когда какие-то вылазки за город по абсолютно рендомному маршруту. Так вот самая боль - это то, что массу фоток потом хочется описать и выложить в блоге. Но это время. Да, можно выложить все фотки на пикаске и под каждой откомментить что-либо. Но это по моему не то - хочу, чтобы отчет был у меня в том месте, где я скажу, а не где-то там на гугле. Потому приходится выравнивать фоточки в HTML редакторе блога, миксовать их с текстом, перечитывать, поправлять. Это рутина. Ее либо автоматизировать стоит, либо ускорить. Вот потому я и начал писать свой проектик. Кроме того свой pet-проектик всегда можно расширить и дополнить теми фичами, которые хочется именно тебе, а не искать/ждать выхода плагинов на какой-то платформе (тот же гугл/блогспот). Да и развиваю навыки кодописательства с каждым таким домашним проектиком. И конечно же могу пошарить все, что узнал сам на страничках в блоге. Пусть эти посты кого-то вдохновят на личные подвиги.

Итак один рывок вчера сделал - проект уже можно задемить. Есть 4 часа видео, которое конечно же raw не стоит выкладывать (я жеж говорил, что хочу совершенстоваться), а потому я буду выкладывать его частями после монтажа. Почему монтажом заниматься важно? Потому что в ходе монтажа ты получаешь обратную связь о том, как стоило писать видео - и это повлияет на качеству будущих записей. Уверен когда-нибудь я смогу выкладывать raw и без монтажа - и он будет классный. А пока так. 

И да, если я сегодня что-нибудь выложу - потом к этому более вероятнее вернусь.

Продолжение обязательно следует. Отпиши пожалуйста +1 в коммент, если это интересно - подлей масла в огонь :).

пятница, 28 марта 2014 г.

Простой возвращатель html файлов

Задачка была сделать так, чтобы по урлу http://127.0.0.1:8080/context вытаскивалось содержимое папки context корня проекта (html/css/js). Причем если я соберу war И задеплою на сервак, то корнем проекта будет считаться корневая папка вебконтейнера. Если запрашиваемого файла нет, создаю новый.

Велосипед короче очередной. Но надо было быстро.

Вот архив.

Вот основной сервлет

package com.apofig;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

public class SampleServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        try {

            String context = req.getContextPath();
            if (context.startsWith("/")) context = context.substring(1);

            String path = req.getServletPath();
            if (path.equals("/") || path.equals("")) path = "/index.html";

            String fileName = context + path;
            File file = new File(fileName);
            if (!file.exists()) {
                file.createNewFile();
            }

            String mimeType = getServletContext().getMimeType(file.getPath());
            resp.setContentType(mimeType);
            resp.setContentLength((int) file.length());

            if (mimeType == null) {
                resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }

            write(resp, file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // thanks to http://stackoverflow.com/questions/8623709/output-an-image-file-from-a-servlet
    private void write(HttpServletResponse resp, File file) {
        try {
            OutputStream out = null;
            FileInputStream in = null;
            try {
                in = new FileInputStream(file);
                out = resp.getOutputStream();

                // Copy the contents of the file to the output stream
                byte[] buf = new byte[1024];
                int count = 0;
                while ((count = in.read(buf)) >= 0) {
                    out.write(buf, 0, count);
                }
            } finally {
                if (out != null) out.close();
                if (in != null) in.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
}

Вот web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <display-name>WebSocketServer</display-name>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>com.apofig.SampleServlet</servlet-class>
        <load-on-startup>100</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

И pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>WebServer</groupId>
    <artifactId>WebServer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <jetty.version>8.1.10.v20130312</jetty.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-client</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-jsp</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>${jetty.version}</version>
                <configuration>
                    <webApp>
                        <contextPath>/context</contextPath>
                    </webApp>
                    <stopPort>9999</stopPort>
                    <stopKey>foo</stopKey>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <encoding>utf8</encoding>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-jspc-maven-plugin</artifactId>
                <version>${jetty.version}</version>
                <executions>
                    <execution>
                        <id>jspc</id>
                        <goals>
                            <goal>jspc</goal>
                        </goals>
                        <configuration>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <webXml>${basedir}/target/web.xml</webXml>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>

Проект собирается
     mvn clean
     mvn package
     mvn war:war
Стартовать его можно
     mvn jetty:run

Может пригодится кому

четверг, 13 марта 2014 г.

Maven Jetty WebSocket server sample

Пусть тут пока полежит... Качаем

Кстати это то, чего практически не делают на просторах сети. Ну разве так сложно выложить вместе с кусками кода в блоге еще и архив с проектом? Намерен эту ошибку интернета исправлять примером.

Что для этого надо?

Сердце любой веб-аппки. Говорим, что будет один сервлет, он будет отвечать на запросы типа http://127.0.0.1:8080/ws

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <display-name<WebSocketServer</display-name>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>wsServlet</servlet-name>
        <servlet-class>com.apofig.SampleWebSocketServlet</servlet-class>
        <load-on-startup>100</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>wsServlet</servlet-name>
        <url-pattern>/ws/*</url-pattern>
    </servlet-mapping>
</web-app>

Сервлет обработчик

package com.apofig;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;

import javax.servlet.http.HttpServletRequest;

public class SampleWebSocketServlet extends WebSocketServlet {

    @Override
    public WebSocket doWebSocketConnect(HttpServletRequest httpRequest, String protocol) {
        final String id = httpRequest.getParameter("id");
        return new WebSocket.OnTextMessage() {

            @Override
            public void onMessage(String data) {
                System.out.println(id + " message > " + data);
            }

            @Override
            public void onOpen(WebSocket.Connection connection) {
                System.out.println(id + " open");
            }

            @Override
            public void onClose(int closeCode, String message) {
                System.out.println(id + " close");
            }
        };
    }
}

Welcome page для тестирования подключения
<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html;charset=UTF8">
<html>
<head>
    <title>Websocket Sample</title>
    <script>
        // Спасибо http://learn.javascript.ru/websockets
        var id = Math.floor(Math.random()*10000);
        var socket = new WebSocket("ws://127.0.0.1:8080/ws?id=" + id);
        socket.onopen = function() {
            alert("Соединение установлено для id=" + id);

            var data = "some data";
            socket.send(data);
            alert("Отправлены данные '" + data + "'");
        };

        socket.onclose = function(event) {
            if (event.wasClean) {
                alert('Соединение закрыто чисто');
            } else {
                alert('Обрыв соединения'); // например, "убит" процесс сервера
            }
            alert('Код: ' + event.code + ' причина: ' + event.reason);
        };

        socket.onmessage = function(event) {
            alert("Получены данные " + event.data);
        };

        socket.onerror = function(error) {
            alert("Ошибка " + error.message);
        };
    </script>
</head>
<body>

</body>
</html>

Main класс - раннер
package com.apofig;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

public class Main {

    public static void main(String[] args) throws Exception{
        String webappDirLocation = "src/main/webapp/";

        String webPort = System.getenv("PORT");
        if(webPort == null || webPort.isEmpty()) {
            webPort = "8080";
        }

        Server server = new Server(Integer.valueOf(webPort));
        WebAppContext root = new WebAppContext();

        root.setContextPath("/");
        root.setDescriptor(webappDirLocation+"/WEB-INF/web.xml");
        root.setResourceBase(webappDirLocation);
        root.setParentLoaderPriority(true);

        server.setHandler(root);
        server.start();
        server.join();
    }
}

Конечно же это все можно запустить плагином maven, а потому вот pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>WebSocketServer</groupId>
    <artifactId>WebSocketServer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <jetty.version>8.1.10.v20130312</jetty.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-client</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-jsp</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-websocket</artifactId>
            <version>${jetty.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>${jetty.version}</version>
                <configuration>
                    <webApp>
                        <contextPath>/</contextPath>
                    </webApp>
                    <stopPort>9999</stopPort>
                    <stopKey>foo</stopKey>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <encoding>utf8</encoding>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-jspc-maven-plugin</artifactId>
                <version>${jetty.version}</version>
                <executions>
                    <execution>
                        <id>jspc</id>
                        <goals>
                            <goal>jspc</goal>
                        </goals>
                        <configuration>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <webXml>${basedir}/target/web.xml</webXml>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

Уверен пригодится...

четверг, 17 октября 2013 г.

Тестируем на Groovy: Шаблончик Spring MVC веб приложения (Maven + Jetty) но с тестами на Groovy (WebDriver)

Такой же как и вчера примерчик, только теперь там тесты на груви (скачать исходник).

Немного статей собрал по ходу. Для создания чистого groovy проекта с нуля может помочь статья. Вот тут можно прочитать о том как добавить Groovy maven plugin в pom.xml для компиляции скриптов groovy в своем приложении. Можно качнуть (и распаковать в c:/java рядом с jdk) Groovy SDK, только я не уверен нужна ли она для maven проекта - один раз IDEA меня спросила про него, я и указал место жительства. Вот тут можно найти несколько книг по Groovy. А вот еще много быстрой инфы о Groovy.

Самое печальное, что потребуется сделать - вернуться к старой версии запуска юнит тестов (< 4) и экстендится от GroovyTestCase а так же начинать названия тестовых методов с test.
import groovy.util.GroovyTestCase

class MyTest extends GroovyTestCase {
 
  void testMyTestMethod() {
  }

}
Но это мелочи по сравнению с теми возможностями, какие дает groovy. Вот хорошая вводная статья по возможностям groovy .

Зачем это все?  Вот немного замечаний из прошлого опыта:

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

+ для начала достаточно создать source/test папку groovy, после в нее можно переместить часть классов/тестов переименовав расширение файлов c java на groovy. И продолжить работу. Обычно скомипилированный java код не вызывает ошибок компиляции на groovy.

+ все что можно было делать на java - все библиотеки доступны так же и для groovy.

для любителей дебага - IDEA хорошо справляется и с этой задачей.

+ используя такую архитектурку я укрепляюсь в зании еще одного языка программирования. Это плюс, как ни крути.

- не все рефакторинги доступны. А ты точно всеми-пре-всеми пользовался? Я нет, мне главное самые примитивные (extract method/variable/constant/class, move ..., inline ...) чтобы были.

- (он же и +) не всегда компилятор подсказывает, что я не прав приходится чаще запускать тесты - это уже плюс. Тесты хочется делать более быстрыми, а запускать часто тесты хорошая практика по борьбе с дебагом.

- немного тебя ломать прийдется изучая что-то новое. Чтобы привыкнуть к языку рекомендую такое упражнение. Вычитываешь какую-то фичу, например - ставить точку с запятой в конце каждой строчки не обязательно, так же как писть return в последней строчке, возвращающей из метода результат. Берешь и во всех классах заменяешь это чтобы было под один стиль. Многократно совершаемое действие лучше отложится в памяти. А завтра можно будет попробовать что-то еще...

- ейфория от возможностей языка вероятно породит на начальном пути порцию говнокодо. С груви легче выстрелить себе в ногу.

Простой пример, зачем это. Вот метод на джаве
    public List<Item> getItems() {
        List<String> result = new LinkedList<String>();
        List<WebElement> elements = getFlightElements();
        for (WebElement element : elements) {
            result.add(new Item(element.getText()));
        }
        return result;
    }
А вот тот же метод на groovy
    List<Item> getItems() {
        getFlightElements().collect {
            new Item(it.text)
        }
    }
И это только цветочки...
Дальше возьмемся за Scala - говорят она круче!