Notificaciones personalizadas con WebSocket
Published: 2023-12-15 14:47:28

En algunas ocasiones necesitamos enviar notificaciones personalizadas a usuarios específicos para indicarles que ha ocurrido una opción o de manera automática actualizar algún componente tal como un tablero, por ejemplo.




![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/081y2q0dxlpquf4yotss.png)
  

Para utilizar WebSocket en nuestras aplicaciones Jakarta EE, seguimos los siguientes pasos:

- Configurar el archivo web.xml
```java

<context-param>
 <param-name>jakarta.faces.ENABLE_CDI_RESOLVER_CHAIN</param-name>
     <param-value>true</param-value>
</context-param>
<context-param>
 <param-name>jakarta.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name>
   <param-value>true</param-value>
</context-param> 
```



## microprofile-config.properties

En el archivo micropofile-config.properties agregue la propìedad

```
#------------------------------------
#--WebSocket
#-------------------------------------
websocket.minimums.seconds.for.update=50

```
En la cual se especifica el tiempo mínimo en minutos en los cuales se debe actualizar el formulario actual , una vez que se invoque el Websocket. Se establece este tiempo para evitar actualizaciones demasiado frecuentes que pueden causar afectaciones en el desempeño.
Si el tiempo es menor que el tiempo establecido, se incrementara el contador de notificaciones pendientes que se mostrara al usuario para que luego pueda dar clic en el botón de notificaciones y se actualice el formulario actual.





## Defina la interface

```java
public interface WebSocketPlantilla {

    public void sendMessageAll(String message, String scoped);

    public void sendMessageProyecto(String message, User user);

    public void sendMessageProyectoMultiple(String message, List<UserView> userViewList);
    
    
    public void sendMessageSprint(String message, User user);

    public void sendMessageSprintMultiple(String message, List<UserView> userViewList);

    public void sendMessageTablero(String message, User user);

    public void sendMessageTableroMultiple(String message, List<UserView> userViewList);

    public void sendMessageBacklog(String message, User user);

    public void sendMessageBacklogMultiple(String message, List<UserView> userViewList);
    
    public void sendMessagePapeleraReciclaje(String message, User user);

    public void sendMessagePapeleraReciclajeMultiple(String message, List<UserView> userViewList);
    
    public void sendMessagePanelTrabajo(String message, User user);

    public void sendMessagePanelTrabajoMultiple(String message, List<UserView> userViewList);

    public Collection<Long> collectionOfUserList(List<UserView> userList);
}
```


## Cree la clase
```java
@ViewScoped
@Named
public class WebSocketController implements Serializable, WebSocketPlantilla {

    @Inject
    @Push
    private PushContext proyectoChannel;
    
    @Inject
    @Push
    private PushContext proyChannel;

    @Inject
    @Push
    private PushContext sprintChannel;

    @Inject
    @Push
    private PushContext tableroChannel;

    @Inject
    @Push
    private PushContext backlogChannel;
  
    @Inject
    @Push
    private PushContext panelTrabajoChannel;
    
    @Inject
    @Push
    private PushContext papeleraReciclajeChannel;

    @Inject
    @Push
    PushContext allChannel;

    /**
     *
     * @param message
     * @param user
     */
    @Override
    public void sendMessageProyecto(String message, User user) {
        proyectoChannel.send(message, user.getIduser());

    }

    /**
     * *
     * Envia mensaje a muchos usuarios
     *
     * @param message
     * @param userViewList
     */
    @Override
    public void sendMessageProyectoMultiple(String message, List<UserView> userViewList) {
        proyectoChannel.send(message, collectionOfUserList(userViewList));
    }
    
    /**
     *
     * @param message
     * @param user
     */
    @Override
    public void sendMessageSprint(String message, User user) {
        sprintChannel.send(message, user.getIduser());

    }

    /**
     * *
     * Envia mensaje a muchos usuarios
     *
     * @param message
     * @param userViewList
     */
    @Override
    public void sendMessageSprintMultiple(String message, List<UserView> userViewList) {

        sprintChannel.send(message, collectionOfUserList(userViewList));
    }

    /**
     *
     * @param message
     * @param user
     */
    @Override
    public void sendMessageTablero(String message, User user) {

        tableroChannel.send(message, user.getIduser());

    }

    /**
     * *
     * Envia mensaje a muchos usuarios
     *
     * @param message
     * @param userViewList
     */
    @Override
    public void sendMessageTableroMultiple(String message, List<UserView> userViewList) {

        tableroChannel.send(message, collectionOfUserList(userViewList));
    }

    @Override
    public void sendMessageAll(String message, String scoped) {
        allChannel.send(message, "user");
    }

   

    @Override
    public void sendMessageBacklog(String message, User user) {
    backlogChannel.send(message, user.getIduser());
    }

    @Override
    public void sendMessageBacklogMultiple(String message, List<UserView> userViewList) {
    backlogChannel.send(message, collectionOfUserList(userViewList));
    }

    @Override
    public void sendMessagePapeleraReciclaje(String message, User user) {
         papeleraReciclajeChannel.send(message, user.getIduser());
    }

    @Override
    public void sendMessagePapeleraReciclajeMultiple(String message, List<UserView> userViewList) {
     papeleraReciclajeChannel.send(message, collectionOfUserList(userViewList));
    }
    
    @Override
    public void sendMessagePanelTrabajo(String message, User user) {
         panelTrabajoChannel.send(message, user.getIduser());
    }

    @Override
    public void sendMessagePanelTrabajoMultiple(String message, List<UserView> userViewList) {
     panelTrabajoChannel.send(message, collectionOfUserList(userViewList));
    }

    
    
     @Override
    public Collection<Long> collectionOfUserList(List<UserView> userViewList) {
        Collection<Long> result = new ArrayList<>();
        try {
            userViewList.forEach(u -> {
                result.add(u.getIduser());
            });
        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return result;
    }
    
    public Collection<Long> collectionOfProyectoList(List<Proyecto> proyectoList) {
        Collection<Long> result = new ArrayList<>();
        try {
            proyectoList.forEach(u -> {
                result.add(u.getIdproyecto());
            });
        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return result;
    }
}

```

## Crear la interface FacesWebSocket

```java

public interface FacesWebSocket {

public String refreshFromWebsocket();

public String updateForRemoteWebsocket();

public void sendMensajesWebsocket(MessageWebSocket messageWebSocket,Tarjeta tarjeta, Boolean exclude);


public void sendMensajesWebsocket(MessageWebSocket messageWebSocket);

public void onCompleteWebSocket();

    /**
     *
     * @param userViewProjectoSelectedForWebSocketList
     * @param tarjeta
     * @return La lista de userView para ser usada en el envio de WebSocket.
     * Verifica si es una tarjeta foranea y agrega el usuario.
     */
 public default List<UserView> filterUserViewListForWebSocket(List<UserView> userViewProjectoSelectedForWebSocketList, Tarjeta tarjeta) {
        List<UserView> result = new ArrayList<>();
        try {
            result.addAll(userViewProjectoSelectedForWebSocketList);
            /**
             * si es una tarjeta foranea agrega el usuario
             */
            if (tarjeta.getForeaneo()) {
                UserView uv = tarjeta.getUserView().get(0);

                Optional<UserView> userViewOptional = result.stream().filter(x -> x.getIduser().equals(uv.getIduser())).findFirst();
                if (!userViewOptional.isPresent()) {

                    result.add(uv);
                }
            }
        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }

        return result;
    }

    // <editor-fold defaultstate="collapsed" desc="List<UserView> filterUserViewListForWebSocket(List<UserView> userViewProjectoSelectedForWebSocketList) {">
    public default List<UserView> filterUserViewListForWebSocket(List<UserView> userViewProjectoSelectedForWebSocketList) {
        List<UserView> result = new ArrayList<>();
        try {
            result.addAll(userViewProjectoSelectedForWebSocketList);

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return result;
    }
// </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="List<UserView> userViewFromProjectExclude(Proyecto proyectoSelected, Boolean exclude, User... excludeUser)">

    /**
     *
     * @param proyectoSelected
     * @return Obtiene la lista de userView del proyecto
     */
    public default List<UserView> userViewFromProjectExclude(Proyecto proyectoSelected, Boolean exclude, User... excludeUser) {
        List<UserView> result = new ArrayList<>();
        try {
            Long iduser = 0l;
            if (excludeUser.length != 0) {
                iduser = excludeUser[0].getIduser();
                for (ProyectoMiembro pm : proyectoSelected.getProyectoMiembro()) {
                    if (exclude) {
                        if (iduser.equals(pm.getUserView().getIduser())) {
                        } else {
                            result.add(pm.getUserView());
                        }
                    } else {
                        result.add(pm.getUserView());
                    }

                }
            } else {
                proyectoSelected.getProyectoMiembro().forEach(pm -> {
                    result.add(pm.getUserView());
                });
            }

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return result;
    }

    // </editor-fold>   
    // <editor-fold defaultstate="collapsed" desc="List<UserView> userViewFromProjectExcludeIncludeOldUser(Proyecto proyectoSelected, List<ProyectoMiembro> proyectoMiembroOldList, Boolean exclude, User... excludeUser)">
    /**
     *
     * @param proyectoSelected
     * @return Obtiene la lista de userView del proyecto
     */
    public default List<UserView> userViewFromProjectExcludeIncludeOldUser(Proyecto proyectoSelected, List<ProyectoMiembro> proyectoMiembroOldList, Boolean exclude, User... excludeUser) {
        List<UserView> result = new ArrayList<>();
        try {
            List<ProyectoMiembro> proyectoMiembroList = new ArrayList<>();
            proyectoMiembroList.addAll(proyectoSelected.getProyectoMiembro());

            proyectoMiembroOldList.forEach(pm -> {
                Optional<ProyectoMiembro> proyectoMiembroOptional = proyectoSelected.getProyectoMiembro().stream().filter(x -> x.getUserView().getIduser().equals(pm.getUserView().getIduser())).findFirst();
                if (!proyectoMiembroOptional.isPresent()) {
                    proyectoMiembroList.add(pm);
                }
            });
            /*
             */

            Long iduser = 0l;
            if (excludeUser.length != 0) {
                iduser = excludeUser[0].getIduser();
                for (ProyectoMiembro pm : proyectoMiembroList) {
                    if (exclude) {
                        if (iduser.equals(pm.getUserView().getIduser())) {
                        } else {
                            result.add(pm.getUserView());
                        }
                    } else {
                        result.add(pm.getUserView());
                    }

                }
            } else {
                proyectoMiembroList.forEach(pm -> {
                    result.add(pm.getUserView());
                });
            }

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return result;
    }

    // </editor-fold>   
    // <editor-fold defaultstate="collapsed" desc="List<UserView> userViewForWebSocketListFromProjectOfTarjeta(Tarjeta tarjeta, List<Proyecto> proyectoPorColaboradorList, User userLogged)">
    public default List<UserView> userViewForWebSocketListFromProjectOfTarjeta(Tarjeta tarjeta, List<Proyecto> proyectoPorColaboradorList, User userLogged) {
        List<UserView> result = new ArrayList<>();
        try {
            Proyecto proyectoWebSocket = new Proyecto();
            Boolean found = Boolean.FALSE;
            for (Proyecto p : proyectoPorColaboradorList) {
                if (p.getIdproyecto().equals(tarjeta.getIdproyecto())) {
                    proyectoWebSocket = p;
                    found = Boolean.TRUE;
                    break;
                }
            }
            if (found) {
                result = userViewFromProjectExclude(proyectoWebSocket, Boolean.FALSE, userLogged);
            }

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return result;
    }
    // </editor-fold>    

    public default String validarEstadoIniciadoYFechaProyecto(Proyecto proyecto, JmoordbResourcesFiles rf) {
        String result = "";
        try {
        
            if (!proyecto.getEstado().equals("iniciado")) {
               result = rf.fromMessage("warning.proyectoestadonoiniciado");
         
            }

            if ((JmoordbCoreDateUtil.fechaIgual(JmoordbCoreDateUtil.getFechaHoraActual(), proyecto.getFechainicial())
                    || JmoordbCoreDateUtil.fechaMayor(JmoordbCoreDateUtil.getFechaHoraActual(), proyecto.getFechainicial()))
                    && (JmoordbCoreDateUtil.fechaIgual(JmoordbCoreDateUtil.getFechaHoraActual(), proyecto.getFechafinal())
                    || JmoordbCoreDateUtil.fechaMenor(JmoordbCoreDateUtil.getFechaHoraActual(), proyecto.getFechafinal()))) {
                
              
            } else {
                result = rf.fromMessage("warning.fechasfuerarangoproyecto");

            }
        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return result;
    }
}


```




## Edite el template.xhtml

Añada funciones Javascript para procesar las respuestas de los Websocket. Se invocaran remoteCommand pasandole los parámetros evento, channel,message.

Estos p:remoteCommand, se deben agregar en cada formulario donde se desea validar el evento.

```js
 <script>
   function handleMessageAllChannel(message, channelName, event) {
        var messageText = message.toString();
        var eventText = event.toString();
        var channelText = channelName.toString();
        remoteCommandWebSocketAll(
                  [
                    {name: 'event', value: eventText},
                    {name: 'channel', value: channelText},
                    {name: 'message', value: messageText}
                  ]);
  }

 function handleMessageProyectoChannel(message, channelName, event) {
       var messageText = message.toString();
       var eventText = event.toString();
       var channelText = channelName.toString();
       remoteCommandWebSocketProyecto(
                [
                 {name: 'event', value: eventText},
                 {name: 'channel', value: channelText},
                 {name: 'message', value: messageText}
                 ]);
            }

 function handleMessageTableroChannel(message, channelName, event) {
      var messageText = message.toString();
      var eventText = event.toString();
      var channelText = channelName.toString();
      remoteCommandWebSocketTablero(
                  [
                    {name: 'event', value: eventText},
                    {name: 'channel', value: channelText},
                    {name: 'message', value: messageText}
                   ]
                 );
           }

function handleMessageSprintChannel(message, channelName, event) {
       var messageText = message.toString();
       var eventText = event.toString();
       var channelText = channelName.toString();
       remoteCommandWebSocketSprint(
                  [
                   {name: 'event', value: eventText},
                   {name: 'channel', value: channelText},
                   {name: 'message', value: messageText}
                   ]);
       }
 
function handleMessageBacklogChannel(message, channelName, event) {
       var messageText = message.toString();
       var eventText = event.toString();
       var channelText = channelName.toString();
       remoteCommandWebSocketBacklog(
                  [
                   {name: 'event', value: eventText},
                   {name: 'channel', value: channelText},
                   {name: 'message', value: messageText}
                   ]);
      }

function handleMessagePapeleraReciclajeChannel(message, channelName, event) {
        var messageText = message.toString();
        var eventText = event.toString();
        var channelText = channelName.toString();
        remoteCommandWebSocketPapeleraReciclaje(
                [
                  {name: 'event', value: eventText},
                  {name: 'channel', value: channelText},
                  {name: 'message', value: messageText}
                 ]);
  }

function handleMessagePanelTrabajoChannel(message, channelName, event) {
      var messageText = message.toString();
      var eventText = event.toString();
      var channelText = channelName.toString();
      remoteCommandWebSocketPanelTrabajo(
                   [
                     {name: 'event', value: eventText},
                     {name: 'channel', value: channelText},
                     {name: 'message', value: messageText}
                   ]);
   }


function errorListener(code, channel, event) {
    if (code == 1001) {
            //  alert('error 1001 Server shutdwon');
                
    } else if (code == 1006) {
              //alert('error 1006 ');
              } else {
                    // alert('otro ');
                
                }
          
            }
</script>

```

Añada componentes Websocket, dentro de un componente <h:form>

```html

 <h:form>        

 <f:websocket channel="allChannel"  user="user" onmessage="handleMessageAllChannel" />

<f:websocket channel="tableroChannel" scope="session"  user="#{loginFaces.userLogged.iduser}" onmessage="handleMessageTableroChannel"
                             onerror="errorListener"/>

 <f:websocket channel="sprintChannel" scope="session"  user="#{loginFaces.userLogged.iduser}" onmessage="handleMessageSprintChannel"
                             onerror="errorListener"/>

 <f:websocket channel="backlogChannel" scope="session"  user="#{loginFaces.userLogged.iduser}" onmessage="handleMessageBacklogChannel" 
                             onerror="errorListener"/>

<f:websocket channel="papeleraReciclajeChannel" scope="session"  user="#{loginFaces.userLogged.iduser}" onmessage="handleMessagePapeleraReciclajeChannel" 
                             onerror="errorListener"/>

<f:websocket channel="panelTrabajoChannel" scope="session"  user="#{loginFaces.userLogged.iduser}" onmessage="handleMessagePanelTrabajoChannel" 
                             onerror="errorListener"/>

<f:websocket channel="proyectoChannel" scope="session"  user="#{loginFaces.userLogged.iduser}" onmessage="handleMessageProyectoChannel" 
                             onerror="errorListener"/>
              

```

### Añada un p:remoteCommnad para atrapar la acción a realizar con el WebSocket.

```xhtml
<p:remoteCommand name="remoteCommandWebSocketAll" 
action="#{loginFaces.webSocketListenerMethod}" 
 update="template_growl_timesession" 

onerror="errorListener"/>

```

### Edite la Clase Dashboard.java

Agregue los atributos para manejar el WebSocket

```java
// <editor-fold defaultstate="collapsed" desc="WebSocket">

    @Inject
    WebSocketController webSocketController;
    private Boolean dialogVisibleWebSocket = Boolean.FALSE;
    private Boolean dialogVisibleAddWebSocket = Boolean.FALSE;
    private List<UserView> userViewForWebSocketList = new ArrayList<>();
    private Integer contadorNotificacionesWebsocket = 0;
    private Long jmoordbCronometerOld = 0L;

    // </editor-fold>  

```

* Leer la cantidad de segundos mínimos establecido en el archivo microprofile-config.properties.

```java

// #Websocket    
    @Inject
    @ConfigProperty(name = "websocket.minimums.seconds.for.update")
    private Provider<Integer> websocketMinimumsSecondsForUpdate;
```


## En el método init()

```java
@PostConstruct
    public void init() {
        try {
            JmoordbCronometer.startCronometer(FacesUtil.nameOfClassAndMethod());
  /**
             * WebSocket
             */
contadorNotificacionesWebsocket = 0;
jmoordbCronometerOld = 0L;
dialogVisibleWebSocket = Boolean.FALSE;
dialogVisibleAddWebSocket = Boolean.FALSE;

...
}

```

## En los métodos prepare

Debe colocar a True el atributo dialogVisibleWebSocket, para que no se actualice si existe un dialogo abierto en que se esta trabajando.
Y Colocar en TRUE al atributo dialogVisibleAddWebSocket solo en el método prepareNew()

```java
public String prepareNew() {
    dialogVisibleWebSocket = Boolean.TRUE;
    dialogVisibleAddWebSocket = Boolean.TRUE;
}
```

El los demás métodos prepare no se utiliza dialogVisibleAddWebSocket
```java
public String prepareEditar(Proyecto proyecto) {
        try {

            dialogVisibleWebSocket = Boolean.TRUE;
}
}
```

Recuerde que en cada método que se invoque desde un dialogo debe asignar el valor a False, este proceso se puede simplificar agregándolo al método refresh() que es invocado por todos los métodos.
 

### En el método refresh()

```java

public String refresh() {

     contadorNotificacionesWebsocket = 0;
     dialogVisibleWebSocket = Boolean.FALSE;
    dialogVisibleAddWebSocket = Boolean.FALSE;
}
```

### En el método save()

Vamos a enviar notificaciones mediante Websocket a varios elementos tales como los canales de proyecto, tablero entre otros.
Creamos un objeto de tipo MessageWebSocket. Que esta incluido en la librería **jmoordbcoreutilfaces**.

```java
public void save(Proyecto proyecto) {

 /**
  * WebSocket
 */
userViewForWebSocketList = userViewFromProjectExclude(proyecto, Boolean.FALSE, userLogged);

 MessageWebSocket messageWebSocket = new MessageWebSocket.Builder()
                        .producer(FacesUtil.nameOfClass())
                        .action(FacesUtil.nameOfMethod())
                        .key("proyecto")
                        .value(proyecto.getIdproyecto().toString())
                        .message("Clonar proyecto")
                        .iduser(userLogged.getIduser())
                        .date(JmoordbCoreDateUtil.fechaHoraActual())
                        .build();
                sendMensajesWebsocket(messageWebSocket);

}



```


### Defina el método sendMensajesWebsocket(MessageWebSocket messageWebSocket)

En este caso se escribirán notificaciones a varios canales.
```java

 @Override
public void sendMensajesWebsocket(MessageWebSocket messageWebSocket) {
    try {
        List<UserView> result = filterUserViewListForWebSocket(userViewForWebSocketList);

            webSocketController.sendMessageProyectoMultiple(messageWebSocket.toJSON(), result);

            webSocketController.sendMessageTableroMultiple(messageWebSocket.toJSON(), result);
            webSocketController.sendMessageSprintMultiple(messageWebSocket.toJSON(), result);
            webSocketController.sendMessagePapeleraReciclajeMultiple(messageWebSocket.toJSON(), result);
            webSocketController.sendMessageBacklogMultiple(messageWebSocket.toJSON(), result);
            webSocketController.sendMessagePanelTrabajoMultiple(messageWebSocket.toJSON(), result);

 } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
    }
```

## Agregue los métodos para procesar los Websocket


```java
@Override
 public String refreshFromWebsocket() {
    try {
/**
             * Leer los datos del Websocket
             */
            String event = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("event");

            String channel = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("channel");

            String message = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("message");

            MessageWebSocket mws = new MessageWebSocket();
            mws = mws.toMessageWebSocket(message);


        if (jmoordbCronometerOld.equals(0L)) {
             if (!dialogVisibleWebSocket) {
                refresh();
                PrimeFaces.current().ajax().update("form");
              }
          jmoordbCronometerOld = JmoordbCronometer.nanoSecondsNow();
            } else {
              Long dif = JmoordbCronometer.diference(jmoordbCronometerOld, JmoordbCronometer.nanoSecondsNow());
              Long seconds = dif / 1000;
              contadorNotificacionesWebsocket++;
              if (seconds < websocketMinimumsSecondsForUpdate.get().longValue()) {
                } else {
                  if (!dialogVisibleWebSocket) {
                      contadorNotificacionesWebsocket = 0;
                      refresh();
                     PrimeFaces.current().ajax().update("form");
                    }
                }

            jmoordbCronometerOld = JmoordbCronometer.nanoSecondsNow();
            }

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return "";
    }




@Override
public String updateForRemoteWebsocket() {
      try {
          if(dialogVisibleAddWebSocket){
                 closeOverlayPanel("PF('overlayPanelTarjetaAgregar').show()");
          }
          
       if (!dialogVisibleWebSocket) {
              return ":form";
          }
       } catch (Exception e) {           FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return "";
    }


```

## Defina el método onCompleteWebSocket()

```java


    public void onCompleteWebSocket() {
        try {
          
        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }

    }

```


## Edite la pagina profile.xhtml correspondiente al dashboard

Agregue un badge que mostrara la cantidad de notificaciones se han realizado escuchando el WebSocket y no se ha actualizado.

```html
 <p:badge 
    value="#{dashboardFaces.contadorNotificacionesWebsocket}" 
    severity="danger">

 <p:commandLink  styleClass="ml-2" 
   action="#{dashboardFaces.refresh()}"     
   update=":form"
   title="#{core['label.notificaciones']}"
   >
   <i class="pi pi-bell" style="font-size: 2rem"/>

 </p:commandLink>
</p:badge>

```

## Edite la pagina dashboard.xhtml, añada un remoteCommand de la siguiente manera.

```xhtml
 <h:form id="form" prependId="false"  >

<p:growl id="growl"/>

<p:remoteCommand name="remoteCommandWebSocketProyecto" action="#{dashboardFaces.refreshFromWebsocket()}"
                             update="#{dashboardFaces.updateForRemoteWebsocket()}" 
                             oncomplete="#{dashboardFaces.onCompleteWebSocket()}" 

                             />

```




## Escuchar Varios webSocket

En el siguiente ejemplo, tenemos una situación que en el formulario principal al actualizar un proyecto, se debe notificar a todos los usuarios que se encuentren en ese proyecto, y además a usuarios que se encuentren en otro formulario como por ejemplo tablero.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eb22rysqvqkyuegz5e4t.png)






# Tablero

El tablero recibe notificaciones desde otro tablero, pero también puede recibir notificaciones desde el Dashboard, que puede enviar un mensaje como por ejemplo el proyecto fue cerrado.




![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fe3gyfzqcm4eh8vramdz.png)


## Edite el formulario tablero.xhtml

Añada un remoteCommand

```html

<p:remoteCommand name="remoteCommandWebSocketTablero" 
   action="#{tableroFaces.refreshFromWebsocket()}"
   oncomplete="#{tableroFaces.onCompleteWebSocket()}"
   update="#{tableroFaces.updateForRemoteWebsocket()}"
                             />

```

### Edite el profile del tablero

---

Añada un p:badge para mostrar la notificación

```html
<p:badge 
  value="#{tableroFaces.contadorNotificacionesWebsocket}"
  severity="danger">
 

 <p:commandLink  styleClass="ml-2" 
        
  action="#{tableroFaces.refresh()}"

  update=":form"

  title="#{core['label.notificaciones']}" >
    
  <i class="pi pi-bell" style="font-size: 2rem"/>

 </p:commandLink>
</p:badge>

```











## TableroFaces.java

En la clase TableroFaces añada los siguientes atributos


* Leer la cantidad de segundos mínimos establecido en el archivo microprofile-config.properties.

```java

// #Websocket    
    @Inject
    @ConfigProperty(name = "websocket.minimums.seconds.for.update")
    private Provider<Integer> websocketMinimumsSecondsForUpdate;
```



### Añada las propiedades

```java

   // <editor-fold defaultstate="collapsed" desc="WebSocket">
    @Inject
    WebSocketController webSocketController;
    private Boolean dialogVisibleWebSocket = Boolean.FALSE;
    private Boolean dialogVisibleAddWebSocket = Boolean.FALSE;
    private List<UserView> userViewForWebSocketList = new ArrayList<>();
    private Integer contadorNotificacionesWebsocket = 0;
    private Long jmoordbCronometerOld = 0L;

    // </editor-fold>    
```

### En el método init()

```java

 /*
            WebSocket
             */
 contadorNotificacionesWebsocket = 0;
 jmoordbCronometerOld = 0L;
 dialogVisibleWebSocket = Boolean.FALSE;
 dialogVisibleAddWebSocket = Boolean.FALSE;

```

### En el método refresh
```java
   public String refresh() {
        try {
            contadorNotificacionesWebsocket = 0;
            dialogVisibleWebSocket = Boolean.FALSE;
            dialogVisibleAddWebSocket = Boolean.FALSE;
            ...
       }
}
```

## En los métodos prepareNew añada dialogVisibleAddWebSocket

```java
 public String prepareNew() {

     dialogVisibleWebSocket = Boolean.TRUE;
     dialogVisibleAddWebSocket = Boolean.TRUE;
}

```

## En los demás métodos prepare solo agregue dialogVisibleWebSocket

```java
public String prepareEditar(Tarjeta tarjeta) {
        try {
            dialogVisibleWebSocket = Boolean.TRUE;
}
}

```




### Agregue el método refreshFromWebsocket

Puede observar que este método contiene una verificación adicional para saber si fue invocado desde un tablero o si fue desde un Dashboard, en el caso que fue invocado desde un Dashboard, indica que el proyecto pudo haber cambiado y necesita obtener una actualización del proyecto de la base de datos.

```java
 @Override
    public String refreshFromWebsocket() {
        try {

            /**
             * Leer los datos del Websocket
             */
            String event = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("event");

            String channel = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("channel");

            String message = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("message");

            MessageWebSocket mws = new MessageWebSocket();
            mws = mws.toMessageWebSocket(message);

            if (jmoordbCronometerOld.equals(0L)) {

                if (!dialogVisibleWebSocket) {
                   //Valida los datos del WebSocket
                    if (mws.getProducer().equals("DashboardFaces") && mws.getKey().equals("proyecto")
                            && mws.getValue().equals(proyectoSelected.getIdproyecto().toString())) {
                        validateChangeInProject();
                    }

                    refresh();
                    PrimeFaces.current().ajax().update("form");
                }
                jmoordbCronometerOld = JmoordbCronometer.nanoSecondsNow();
            } else {

                Long dif = JmoordbCronometer.diference(jmoordbCronometerOld, JmoordbCronometer.nanoSecondsNow());
                Long seconds = dif / 1000;
                contadorNotificacionesWebsocket++;
                if (seconds < websocketMinimumsSecondsForUpdate.get().longValue()) {

                } else {
                    if (!dialogVisibleWebSocket) {
                        //Valida los datos del WebSocket
                        if (mws.getProducer().equals("DashboardFaces") && mws.getKey().equals("proyecto")
                                && mws.getValue().equals(proyectoSelected.getIdproyecto().toString())) {
                            validateChangeInProject();
                        }
                        contadorNotificacionesWebsocket = 0;
                        refresh();
                        PrimeFaces.current().ajax().update("form");
                    }
                }

                jmoordbCronometerOld = JmoordbCronometer.nanoSecondsNow();
            }

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return "";
    }
```


### Agregar el método updateForRemoteWebsocket()

```java

 public String updateForRemoteWebsocket() {
        try {
           if (dialogVisibleAddWebSocket) {
                
closeOverlayPanel("PF('overlayPanelTarjetaAgregar').show()");
          }

            if (!dialogVisible) {
                return ":form";
            }
        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return "";
    }

```

## Añada el método onCompleteWebSocket()
```java
public void onCompleteWebSocket() {
        try {

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }

    }

```



## Defina el método para validar cambios en el Proyecto

```java
 public String validateChangeInProject() {
        try {
            haveSprintOpen = Boolean.TRUE;
            isSprintVencido = Boolean.FALSE;

            Proyecto proyectoWebSocket = proyectoServices.findByIdproyecto(proyectoSelected.getIdproyecto());
            if (proyectoSelected.equals(proyectoWebSocket)) {

            } else {

                JmoordbCoreUtil.copyBeans(proyectoSelected, proyectoWebSocket);

                JmoordbCoreContext.put("DashboardFaces.proyectoSelected", proyectoSelected);
                String result = validarEstadoIniciadoYFechaProyecto(proyectoSelected, rf);
                if (!result.equals("")) {

                    haveSprintOpen = Boolean.FALSE;
                    isSprintVencido = Boolean.TRUE;
                    message = result;
                    FacesUtil.warningDialog(result);
                    PrimeFaces.current().ajax().update("form");

                }

            }

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
        return "";
    }

```







En el método save, realice las operaciones y obtenga la lista de usuarios a los que se les debe enviar la notificación y utilice el método sendMessageTableroMultiple.

```java
 public void save(Tarjeta tarjeta) {

MessageWebSocket messageWebSocket = new MessageWebSocket.Builder()
                        .producer(FacesUtil.nameOfClass())
                        .action(FacesUtil.nameOfMethod())
                        .key("tarjeta")
                        .value(tarjeta.getIdtarjeta().toString())
                        .message("Crear tarjeta")
                        .iduser(userLogged.getIduser())
                        .date(JmoordbCoreDateUtil.fechaHoraActual())
                        .build();
                sendMensajesWebsocket(messageWebSocket,tarjeta, Boolean.FALSE);
}
```


### Añada el método
```java

@Override
    public void sendMensajesWebsocket(MessageWebSocket messageWebSocket,Tarjeta tarjeta, Boolean exclude) {
        try {
            List<UserView> result = filterUserViewListForWebSocket(userViewForWebSocketList, tarjeta);
            webSocketController.sendMessageTableroMultiple(messageWebSocket.toJSON(), result);
            webSocketController.sendMessageBacklogMultiple(messageWebSocket.toJSON(), result);
            webSocketController.sendMessagePapeleraReciclajeMultiple(messageWebSocket.toJSON(), result);
            webSocketController.sendMessagePanelTrabajoMultiple(messageWebSocket.toJSON(), result);

        } catch (Exception e) {
            FacesUtil.errorMessage(FacesUtil.nameOfClassAndMethod() + " " + e.getLocalizedMessage());
        }
    }

```



En el dialogo que usamos para agregar imágenes

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9owamnq9mjqxzge3rvkz.png)

Invocar los métodos
```java
  <p:commandButton  value="#{core['button.save']}" icon="pi pi-save"
  action="#{tableroFaces.save(tableroFaces.tarjetaSelected)}"
  update=":form:growl"
  process="#{cc.attrs.id}"                                                 
  styleClass="w-6 ml-2"/>

                
   <p:commandButton value="#{core['button.cancel']}" icon="pi pi-times"                                               onclick="PF('overlayPanelTarjetaAgregar').hide()"
action="#{tableroFaces.refresh()}"                              
 update=":form"                                                   
 
styleClass="ui-button-outlined w-6 mr-2"/>
```