Java broadcast message websocket

websocketAcredito que possa ser útil dar uma olhada no WebSockets, que é uma tecnologia relativamente nova que promete tornar sites mais reativos, permitindo menor latência entre os usuários e o servidor.
Neste artigo vou mostrar um simples exemplos de envio de mensagem a todos conectado no servidor utilizando Websocket e Java.

O que é WebSocket?

Antes de começar com o código fonte, mas descobrir o que é a tecnologia.
O WebSocket é um protocolo que permite a comunicação entre o cliente e o servidor / nó de extremidade usando uma única conexão TCP.
Parecido com o http. A vantagem que o WebSocket tem sobre o HTTP é que o protocolo é full-duplex (permite a comunicação simultânea em dois sentidos) e seu cabeçalho é muito menor que o de um cabeçalho HTTP,
permitindo uma comunicação mais eficiente mesmo em pequenos pacotes de dados.

O ciclo de vida de um WebSocket é fácil de entender também:

O cliente envia ao servidor uma solicitação de handshake na forma de um cabeçalho de atualização HTTP com dados sobre o WebSocket ao qual está tentando se conectar.
O servidor responde à solicitação com outro cabeçalho HTTP, esta é a última vez que um cabeçalho HTTP é usado na conexão WebSocket. Se o handshake foi bem sucedido, o servidor envia um
cabeçalho HTTP dizendo ao cliente que está mudando para o protocolo WebSocket.
Agora uma conexão constante é aberta e o cliente eo servidor podem enviar qualquer número de mensagens umas às outras até que a conexão seja fechada. Essas mensagens têm apenas cerca de 2 bytes de sobrecarga.

Um Exemplo

A API do WebSocket foi introduzida com o Java EE7, neste exemplo vamos criar dois clientes, uma aplicação web e outra standalone, que enviará uma mensagem para o servidor e o servidor deverá espalhar a mensagem e responder a quem enviou a mensagem.

Neste exemplo, usarei o Eclipse, servidor Tomcat 8 e a biblioteca do nv-websocket-client para o cliente standalone.
Para o seu servidor, crie um aplicação Web.
Converta para um projeto Maven e adicione as seguintes linhas no 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>br.com.tsystems</groupId>
<artifactId>webSocket-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
<java-version>1.8</java-version>
<tomcat.version>8.0.30</tomcat.version>
<debug.code>true</debug.code>
</properties>

<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>

<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>

<dependency>
<groupId>com.neovisionaries</groupId>
<artifactId>nv-websocket-client</artifactId>
<version>1.30</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Crie seu EndPoint, classe Java responsável por receber as mensagens e espalha:


/**
* @ServerEndpoint poderá ser acessado por ws://localhost:8080/webSocket-example/echo
*/
@ServerEndpoint("/echo")
public class WebSocketDemo {
private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());

@OnOpen
public void onOpen(final Session session) {
// Adicionar sessão
sessions.add(session);
}

@OnMessage
public String echo(Session currentSession, String message) {
synchronized (sessions) {
for (Session session : sessions) {
// enviar mensagens para todos os clientes com essão daquele que enviou.
if (session.isOpen() && !session.equals(currentSession)) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
return message;
}

@OnError
public void onError(Session session, Throwable thr) {
//Ocorrer erro
thr.printStackTrace();
}

@OnClose
public void onClose(final Session session) {
// remover a sessão
sessions.remove(session);
}
}

Crie o index.html com o código abaixo:

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
</head>

<body>
<meta charset="utf-8">
<title>Web Socket JavaScript Echo Client</title>
<script language="javascript" type="text/javascript">
 var wsUri = getRootUri() + "/webSocket-example/echo";

function getRootUri() {
 return "ws://" + (document.location.hostname == "" ? "localhost" : document.location.hostname) + ":" +
 (document.location.port == "" ? "8080" : document.location.port);
 }

function init() {
 output = document.getElementById("output");
 }

function send_echo() {

websocket = new WebSocket(wsUri);
 websocket.onopen = function (evt) {
 onOpen(evt)
 };
 websocket.onmessage = function (evt) {
 onMessage(evt)
 };
 websocket.onerror = function (evt) {
 onError(evt)
 };

}

function onOpen(evt) {
 writeToScreen("CONNECTED");
 doSend(textID.value);

}

function onMessage(evt) {
 writeToScreen("RECEIVED: " + evt.data);
 }

function onError(evt) {
 writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
 }

function doSend(message) {
 writeToScreen("SENT: " + message);
 websocket.send(message);
 }

function writeToScreen(message) {
 var pre = document.createElement("p");
 pre.style.wordWrap = "break-word";
 pre.innerHTML = message;
 //alert(output);
 output.appendChild(pre);
 }

window.addEventListener("load", init, false);

</script>
<h2 style="text-align: center;">WebSocket Echo Client</h2>
</br>
<div style="text-align: center;">
<form action="">
<input onclick="send_echo()" value="Press me" type="button">
<input id="textID" name="message" value="Hello WebSocket!" type="text">

</form>
</div>
<div id="output"></div>
</body>
</html>

Por fim, crie o código do cliente stantalone:


/**
* @author Andre Rezende
*/
public class EchoClient {
private static final String SERVER = "ws://localhost:8443/cms/app/echo";
private static final int TIMEOUT = 5000;

public static void main(String[] args) throws Exception {
EchoClient echoClient = new EchoClient();
WebSocket ws = null;
try {
ws = echoClient.connect();
ws.sendText(new Date() + " teste->1");
} finally {
// Close the WebSocket.
ws.disconnect();
}
}

/**
* Conectar no servidor de websocket.
*/
public WebSocket connect() throws IOException, WebSocketException {
return new WebSocketFactory().setConnectionTimeout(TIMEOUT).createSocket(SERVER)
.addListener(new WebSocketAdapter() {
// A text message arrived from the server.
public void onTextMessage(WebSocket websocket, String message) {
System.out.println(message);
}
}).addExtension(WebSocketExtension.PERMESSAGE_DEFLATE).connect();
}
}

Para testar primeiro realize o deploy do seu war no tomcat. Talvez o tomcat requisite a biblioteca tomcat-juli.jar. Ela poderá ser obtida pelo endereço http://www.java2s.com/Code/Jar/t/Downloadtomcatjulijar.htm.
Após realizar o download, descompacte-a no diretório lib do seu tomcat.
Servidor de aplicação (Tomcat) em execução, realizado o deploy sem qualquer erro no tomcat, está na hora de iniciarmos os testes.
Para executar o cliente standalone, basta executar a classe EchoClient. A classe enviará a mensagem “Data corrente ” + test->1.
Abra a aplicação WEB no seu browser pela URL: localhost:8080/webSocket-example/
Por último abra o site: http://www.websocket.org/echo.html. Ele permite realizar testes por web em seu servidor de Websocket. Em location adicione ws://localhost:8080/websocket-example/echo
Clique no botão Connect, escreva em Message a mensagem que deseja compartilhar e por fim clique em Send para enviar a mensagem. Instruções em inglês e um exemplo em HTML está disponível no mesmo link.
Sua aplicação web deverá receber a mesma mensagem enviada pelo último site.

Concluindo

Webscoket permite envio de mensagem de forma assincrona e com segurança.
Substitui a antiga abordagem de consultas aos dados através de refresh de tela ou de tempos em tempos, o que particularmente é ineficiente na maior parte dos casos. Torna a vida do desenvolvedor mais fácil.
É baseado no TCP, bi-derecional e full-duplex. Part do HTML5 e a W3C define a API Jscript. Suportador pela maior parte dos browsers e suas respectivas versões.
Sugestão, comece fazendo um teste simples e vá aumentando a complexidade de seu websocket. Adicione recursos de reconexão e mensagens do mais variados tipos como textos ou binários.
Boa diversão.
O código está disponível para download no Github 

Project Lombok

Cansado de ficar fazendo Gets/Sets em seu código java?
Veja o Project Lombok.
Uma simples anotação em seu código @Data (gerar ate hashCode, equals e toString()) ou @Getter / @Setter poderá obter facilitar essa vida de gerador. Sei que se usar uma IDE tem teclas de atalhos para gerar, mas nada melhor que somente anotar a classe e aguardar o resto.

Na página tem alguns videos explicativos.

Problemas de Upload de arquivos com Primefaces?

Problemas de Upload de arquivos com Primefaces?

Verifique se adicionou em seu web.xml:

    <!-- Muito importante -->
	<context-param>
		<param-name>primefaces.UPLOADER</param-name>
		<param-value>commons</param-value>
	</context-param>	
	
	<filter>
	 <filter-name>PrimeFaces FileUpload Filter</filter-name>
	 <filter-class>
	  org.primefaces.webapp.filter.FileUploadFilter
	 </filter-class>
	</filter>
	<filter-mapping>
	 <filter-name>PrimeFaces FileUpload Filter</filter-name>
	 <servlet-name>Faces Servlet</servlet-name>
	</filter-mapping>
	
	<servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    <context-param>
        <description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>
    <context-param>
        <param-name>primefaces.UPLOADER</param-name>
        <param-value>auto</param-value>
    </context-param>
    <listener>
        <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
    </listener>	
  

A dependencias para upload de arquivos são:

		
		<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <!-- Faces Implementation -->
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.2.4</version>
        </dependency>
        <!-- Faces Library -->
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.2.4</version>
        </dependency>
        <!-- Primefaces Version 5 -->
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>5.0</version>
        </dependency>
		<!-- File uploads -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.2.1</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>1.4</version>
		</dependency>  
		
        <!-- JSP Library -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <!-- JSTL Library -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.1.2</version>
        </dependency>		
	

index.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Journaldev Tutorial</title>
    </h:head>
    <h:body>
        <h:form enctype="multipart/form-data">
                <p:fileUpload value="#{fileUploadManagedBean.file}"  mode="simple"></p:fileUpload>
                <p:separator/>
                <h:commandButton value="Dummy Action" action="#{fileUploadManagedBean.dummyAction}"></h:commandButton>
        </h:form>
    </h:body>
</html>		

FileUploadManagedBean.java:

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
 
import org.primefaces.model.UploadedFile;
 
@ManagedBean
@SessionScoped
public class FileUploadManagedBean {
    UploadedFile file;
 
    public UploadedFile getFile() {
        return file;
    }
 
    public void setFile(UploadedFile file) {
        this.file = file;
    }
 
    public String dummyAction(){
        System.out.println("O nome do arquivo é "+file.getFileName()+" :: Uploaded File Size :: "+file.getSize());
        return "";
    }
}

Conclusão
Este artigo tem intenção de prover uma explicação da utilização do FileUpload do Primefaces. Caso não esteja conseguindo realizar upload, tente verificar se todos os passos estão realizados e não falta nenhum detalhe. O componente FileUpload tem muitas funcionalidades e permite que o programador mantenha o foco do trabalho nas regras de negócio ao invés de ficar reinventando a roda.

Java 8 e StringJoiner

Na ultima versão do Java 8 foi adicionado o StringJoiner no pacote java.util.
É uma implementação juntar as pedaços em comparação, um facilitador da antiga abordagem usando StringBuffer / StringBuilder.
Vamos ver o uso do StringJoiner e como funciona a implementação.

Por exemplo, tenho duas cadeias como “Andre” e “Rezende” e eu quero juntar essas cadeias como [Andre, Rezende]. Neste caso, eu tenho o prefixo como “[“, o sufixo como “]” e o delimitador como “,”. StringJoiner tem dois construtores como dado abaixo.
StringJoiner (CharSequence delimitador)
StringJoiner (CharSequence delimitador, CharSequence prefixo, sufixo CharSequence)
Queremos ter prefixo e sufixo, portanto, vamos usar o segundo construtor para o nosso exemplo.

StringJoiner sjr = new StringJoiner(",", "[", "]");
sjr.add("Andre").add("Rezende");
System.out.println("A string final é " + sjr);

Resultado será a saída no console: A string final é [Andre,Rezende]

Se não quiser adicionar o prefixo e sufixo, então:

StringJoiner sjr1 = new StringJoiner(",");
sjr1.add("Andre").add("Rezende");
System.out.println("A string final é " + sjr1);

Resultado será a saída no console: A string final é [Andre,Rezende]

Conclusão, não é uma baita implementação, ainda pode-se utilizar a StringBuilder ou StringBuffer, mas ele facilita um pouco a vida do desenvolvedor.

Exemplo JDBI

Como já comentado no artigo anterior, JDBI é uma forma de implementar seu DAO com mais produtividade, uma biblioteca para se trabalhar SQL e banco de dados relacionais.
Então vamos criar um simples DAO para realizar operações CRUD(Create, Read, Update e Delete).
Para esse exemplo é necessário possuir o MySQL em execução na sua máquina com o esquema teste criado.

No maven (pom.xml) de seu projeto Java, adicione as dependências do JDBI versão 2.73 , do MYSQL 5.1.38 e Junit versão 4.12 para nossos testes unitários.

                <!-- JDBI-->
		<dependency>
			<groupId>org.jdbi</groupId>
			<artifactId>jdbi</artifactId>
			<version>${jdbi.version}</version>
		</dependency>

                <!-- MySQL -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.38</version>
		</dependency>

                <!-- JUNIT -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>

Criaremos uma interface de nome MyDAO:

public interface MyDAO {}

Agora que temos nossa classe criada, podemos então criar nossos métodos CRUD, vamos implementar uma INSERT, SELECT, UDPATE e DELETE para tabela do MySQL TBLEXEMPLO conforme métodos abaixo:

public interface MyDAO
{
	//Cria tabela TBLEXEMPLO no banco
	@SqlUpdate("CREATE TABLE IF NOT EXISTS TBLEXEMPLO (ID int NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, PRIMARY KEY (ID))")
	void createTable();

	//Insere registro no banco na tabela TBLEXEMPLO
	@SqlUpdate("insert into TBLEXEMPLO (name) values (:name)")
	void insert(@Bind("name") String name);

	//filtra no banco da tabela TBLEXEMPLO
	@SqlQuery("select ID from TBLEXEMPLO where name = :name")
	int findIdByName(@Bind("name") String name);

	//filtra no banco da tabela TBLEXEMPLO
	@SqlQuery("select name from TBLEXEMPLO where id = :id")
	String findNameById(@Bind("id") int id);

	//Altera registro no banco na tabela TBLEXEMPLO
	@SqlUpdate("update TBLEXEMPLO set name = :name where id = :id")
	int updateNameById(@Bind("id") int id, @Bind("name") String name);

	//Apaga registro no banco na tabela TBLEXEMPLO
	@SqlUpdate("delete from TBLEXEMPLO where id = :id")
	int deleteById(@Bind("id") int id);

	//Apaga tabela após conclusão de testes
	@SqlUpdate("drop table TBLEXEMPLO")
	void dropTable();

	//Finaliza conexão
	void close();
}

As anotações @SqlUpdate servem para realizar operações de escrita no branco de dados, em quanto as anotações @SqlQuery para leitura, como SELECT.

Abaixo o código que realiza os testes unitários.

public class MyDAOTest {

	@Test
	public void testJdbi() {
		MyDAO dao = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			//Realiza conexão com o mysql
			DBI dbi = new DBI("jdbc:mysql://localhost/test", "usuario", "senha");
			//Abra conexão com o MySQL
			dao = dbi.open(MyDAO.class);
			//Cria tabela
			dao.createTable();
			//Insere Registro com nome Rezebde
			dao.insert("Rezende");
			int id = dao.findIdByName("Rezende");
			//Nome retornado da consulta deve ser Rezende
			assertEquals(1, id);
			//Altera nome de Rezende para Rezende2
			dao.updateNameById(id, "Rezende2");
			String name2 = dao.findNameById(id);
			//Nome retornado deve ser Rezende2
			assertEquals("Rezende2", name2);

			//Apaga registro criado
			dao.deleteById(id);
			String name3 = dao.findNameById(id);
			//Valor retornado deve ser nulo, pois registro foi apagado acima
			assertNull(name3);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			fail("Error");
		} finally {
			if (dao != null) {
				// Apaga tabela
				dao.dropTable();
				dao.close();
			}
		}
	}
}

Executando os testes acima, não deverá retornar erro.

O download do projeto pode ser feito em meu github: https://github.com/andremrezende/jdbi-example.git

Como visto no post, é muito rápido implementar a camada de persistência utilizando o JDBI e chamadas nativas no banco de dados desejado.

Limpeza de código morto

UCDetector

UCDetector

Trabalhar com manutenção de código não é tarefa fácil.
Depende do local que trabalha, terá tempo para executar testes e melhorar a solução que já existe.
Comecei minha vida de programador Java em 2002, quando aprendi procurando em tutoriais na internet da antiga SUN e trabalhando na área.
Desde lá, Java evolui muito como as IDEs de suporte, exemplo disso é o Eclipse, mas e o código legado? Infelizmente não, porque? Nas empresas onde trabalhei, tempo de refactoring é tempo perdido.
A vantagem de refactoring em código pode ser muito grande, mas o tempo inicial investido pode ser gigantesco. Isso requisita retestar algo que já funciona ou tudo por completo.
O ditado sempre é o mesmo: “Em time que está ganhando não se mexe.”. Será verdade? Ganhando por estar funcionando como planejado inicialmente, mas não tirando o melhor proveito da tecnologia. Evolução é melhoria em meu ponto de vista. Nessa evolução inclui, qualidade, performance, produtividade, dentre outros fatores.
Uma questão da qual eu não vejo muita discussão em refactorings, será que código não utilizado deve ser mantido? N vezes a pessoa que faz manutenção não é a mesma que criou, ou pior, a pessoa criadora daquele código já pode ter abandonado o barco. Comecei então a tirar dos projetos todo código que não é utilizado, tarefa árdua para legados.
No Eclipse achei um plugin que tem me ajudado, o UCDetector. Ele faz a limpeza de código fonte inútil. Busca referências de classe, pacote e até projeto que não possuem nenhuma associação apontando os respectivos problemas ou melhorias.
Sempre que sobra um tempo, executo o UCDector no projeto desejado e realizo a refatoração. Sei muito bem que ela vai me ajudar a não ver código perdido e todos vão ganhar para realizar leitura e manutenções nas aplicações.

Sobre o UCDetector: É um plugin do Eclipse para busca código Java morto. Por exemplo, classe públicas, métodos e propriedade que não tem referência.
Ele cria marcadores para os seguintes melhorias: Código não necessário (morto), código onde a visibilidade por ser alterada (protected, default ou private), métodos ou campos que podem ser final. São apenas sugestões. Tenha certeza das alterações que você irá realizar. As referências ainda podem ser usadas em:
Reflexões, frameworks, código de terceiros, jars, jsp, xml, e outros, alterar visibilidade por causar transtornos e não executar conforme esperado.

Link: http://www.ucdetector.org/

Espero ter ajudado,
André Rezende