Главная > Soft > Heroku.com: как получить реализацию Websockets на java (JSR-365 подход)

Heroku.com: как получить реализацию Websockets на java (JSR-365 подход)

Я уже описывал, как удалось запустить java веб-приложение с Websockets на Heroku. Этот подход основывается на запуске war файла со legacy кодом для tomcat 7. Для современных приложение такое решение не подходит. Хочется, чтоб код использовал JSR 365.

Heroku.com

 

В прошлый раз мне так и не удалось запустить java Websockets с помощью  webapp-runner.jar или jetty-runner.jar. Эту идею пришлось оставить. Но появилась задумка попробовать Embedded Jetty.  В этом случае jetty-runner придётся сделать самостоятельно.

Нам понадобится стандартное Maven приложение. Пример моего приведён ниже.

Jetty-applicationДальше нужно настроить pom.xml

pom.xml

<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>JettyServer</groupId>
	<artifactId>JettyServer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<properties>
	   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	   <jetty.version>9.3.13.v20161014</jetty.version>
	 </properties>  
	<build>
	  <sourceDirectory>src</sourceDirectory>
	  <plugins>
	    <plugin>
	      <artifactId>maven-compiler-plugin</artifactId>
	      <version>3.5.1</version>
	      <configuration>
	        <source>1.8</source>
	        <target>1.8</target>
	      </configuration>
	    </plugin>
		<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <executions>
          <execution>
            <id>src-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>unpack-dependencies</goal>
            </goals>
            <configuration>
              <classifier>sources</classifier>
              <failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
              <outputDirectory>${project.build.directory}/sources</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>	    
	  <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>2.6</version>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>      
	  </plugins>
	</build>	
	<dependencies>
		<dependency>
		    <groupId>org.eclipse.jetty.websocket</groupId>
		    <artifactId>javax-websocket-server-impl</artifactId>
		    <version>${jetty.version}</version>
		</dependency>
	</dependencies>
</project>

подготовить сокет

ProxyWebSocket.java

package info.privateblog.proxy;

import java.io.IOException;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/proxy")
public class ProxyWebSocket {
	@OnOpen
	public void onSessionOpened(Session session) throws IOException {
		System.out.println("onSessionOpened");
		for (int i = 0; i < 30; i++) {
			session.getBasicRemote().sendText("Test: " + i);
			System.out.println("sending...");			
			try {
				Thread.currentThread().sleep(500);
			} catch (InterruptedException e) {}
		}
	}
	@OnMessage
	public void onMessageReceived(String message, Session session) throws IOException {
		System.out.println("onMessageReceived");
	}
	@OnClose
	public void onClose(Session session, CloseReason closeReason){
		System.out.println("onClose");
	}
	@OnError
	public void onErrorReceived(Throwable t) {
		System.out.println("onErrorReceived: " + t);
	}	
}

главная страница остаётся почти такой же, как и в прошлой статье.

index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<title>Index</title>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
                <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
	</head>
<body>
	<div id="ping"></div>
	<script type="text/javascript">
		$(function() {
			var loc = window.location, new_uri;
			if (loc.protocol === "https:") {
			    new_uri = "wss:";
			} else {
			    new_uri = "ws:";
			}
			new_uri += "//" + loc.host;
			new_uri += loc.pathname + "proxy";			
			
		    var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
		    var dateSocket = new WS(new_uri)
	
		    var receiveEvent = function(event) {
		        $("#ping").html("Last ping: "+event.data);
		    }
	
		    dateSocket.onmessage = receiveEvent;
		})
	</script>
</body>
</html>

и runner

Proxy.java

package info.privateblog;

import javax.websocket.server.ServerContainer;

import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;

import info.privateblog.proxy.ProxyWebSocket;

public class Proxy {
	public static void main(String[]args) {
		int port = 80;
		if (args.length > 0) {
			port = Integer.parseInt(args[0]);
		}
		
		Server server = new Server(port);
		//server.setDumpAfterStart(true);
		server.setRequestLog(new RequestLog() {
			@Override
			public void log(Request arg0, Response arg1) {
				System.out.println("Request: " + arg0);
				System.out.println("Response: " + arg1);
			}
		});

		ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
		context.setContextPath("/");
		context.setResourceBase("./res/");
		context.setWelcomeFiles(new String[]{ "index.html" });
		server.setHandler(context);
		
		
		// add special pathspec of "/home/" content mapped to the homePath
        ServletHolder holderHome = new ServletHolder("/", DefaultServlet.class);
        holderHome.setInitParameter("dirAllowed","true");
        holderHome.setInitParameter("pathInfoOnly","true");
        context.addServlet(holderHome,"/*");
        
		try {
			ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
			wscontainer.addEndpoint(ProxyWebSocket.class);
			
			server.start();
			server.join();
		} catch (Exception e) {
			System.out.println("Error.");
			e.printStackTrace();
		}

	}
}

Теперь у нас есть основные элементы приложения. Дальше можно попробовать его собрать и запустить локально

mvn package

java -cp target/JettyServer-0.0.1-SNAPSHOT-jar-with-dependencies.jar info.privateblog.Proxy

Если всё собралось и запустилось, то можно попробовать отправить его на Heroku. Сначала для этого придётся в корень проекта добавить Procfile, который используется для запуска приложения.

Procfile

web:    java $JAVA_OPTS -cp target/JettyServer-0.0.1-SNAPSHOT-jar-with-dependencies.jar info.privateblog.Proxy $PORT

и отправить всё на сервер. Для этого придётся добавить проект в локальный git репозиторий, создать heroku приложение и уже потом сделать push исходного кода.

git init

git add .

git commit -m ‘Initial commit’

heroku create

git push heroku master

Теперь можно попробовать запустить

heroku open

Если не запустилось, то можно попробовать выполнить команду

heroku scale web=1

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

heroku ps

heroku logs -t

Всё!
Heroku.com: как получить реализацию Websockets на java (JSR-365 подход)

Categories: Soft Tags: , , ,
  1. Пока что нет комментариев.
  1. Пока что нет уведомлений.