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


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

четверг, 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>

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

10 комментариев:

  1. Есть такой замечательный фреймворк Atmosphere. По идее работает на любых сервлет-контейнерах. Например Atmosphere используется в некоторых компонентах Primefaces
    https://github.com/Atmosphere/atmosphere

    ОтветитьУдалить
  2. А можете поподробнее пояснить про:
    // TODO тут опасно, class cast на разных версиях jetty при деплое может возникнуть?

    У меня на встроенном в eclipse нормально работает, а при деплое не работает.
    Не уходит сообщение от клиента на сервер. Версия jetty 8.1.13

    ОтветитьУдалить
  3. Возможно в том, что проект деплоится где-то в в дебрях диска C../../..temp/.. и я пока не знаю как это поменять (очень много документации, все сразу не изучишь), больше пока нет идей :)

    ОтветитьУдалить
    Ответы
    1. А опишите, пожалуйста весь процесс запуска проложения, как для бабушки. Чтобы иметь контекст.

      Удалить

    2. eclipse kepler:

      . . . main { . . . server = new Server(8080);
      server.setHandler(new WebAppContext("WebContent", "/MyApp"));
      server.start();
      server.join(); . . . } . . . - Далее -> ServerRunner.java - Run As Java Application - так работает.

      А вот если Project -> Export -> WAR file -> D:\Java\jetty-8.1.13\webapps\MyApp.war ->
      далее батник:
      cd d:\Java\jetty-8.1.13
      d:
      java -jar start.jar - все работает кроме WebSocket, даже Hibernate ;), (просто он тоже не просто так запускается)
      Проект деплоится в C:\Users\my-name\AppData\Local\Temp\jetty-0.0.0.0-8080-MyApp.war-_MyApp-any-\webapp

      Библиотеки добавлены: Project -> Properties -> Java Build Path -> Add Library и часть в WEB-INF/lib для JSTL

      Запускается и первый и второй вариант одинаково - http://my-name:8080/MyApp в FireFox

      Удалить
    3. Можете, пожалуйста, собрть мне в zip сбилдженное web приложение вместе с вашим джетти, батником и всеми всеми запчастями. Попробую у себя поднять. Сложно так сразу сообрзить.

      Мне кажется, что Project -> Export -> WAR не совсем корректный путь. Попробуйте через
      mvn clean -> почистит target
      mvn package -> скомпилит проект
      mvn war:war -> соверет варник, с которым джетти должен подружиться.

      Прием.

      Удалить
    4. Александр Пономарев aleapv@mail.ru5 июня 2014 г. в 08:35

      Собрал проект :
      [url=http://webfile.ru/32f30a04194263ebded68e36905bec17]СкачатьWebSocket.zip[/url]
      СкачатьWebSocket.zip
      (точнее его мини-часть, так как он использует DBOracle). Но проблема уже решена удалением вот этих двух строк:
      Request request = (Request)httpRequest;
      // TODO тут опасно, class cast на разных версиях jetty при деплое может возникнуть
      final String remote = request.getRemoteAddr() + ":" + request.getRemotePort();
      и соответственно переменной remote отовсюду.
      Это я сделал после того как получил Runtime.Error по поводу NoClassDefinition - Request
      в этом маленьком проекте. Значит скорей всего проблема была в библиотеках.

      Пора начинать похоже использовать maven. Большое спасибо за помощь!

      Удалить
    5. Александр, очень рад!
      Мавен не без проблем, но он и вправду вас ускорит после ручной сборки и менеджмента библиотек.
      Ту строчку я удалил из описания в блоге - спасибо!
      Пишите, если что

      Удалить
    6. Обновил исходники:
      1) устранил ClassCast
      2) добавил html/javascript код для демонстрации общения клиент+сервер

      Удалить