DEV Community

aristides villarreal
aristides villarreal

Posted on • Updated on

Notificaciones personalizadas con WebSocket

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

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

  • Configurar el archivo web.xml

<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> 
Enter fullscreen mode Exit fullscreen mode

microprofile-config.properties

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

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

Enter fullscreen mode Exit fullscreen mode

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

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);
}
Enter fullscreen mode Exit fullscreen mode

Cree la clase

@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;
    }
}

Enter fullscreen mode Exit fullscreen mode

Crear la interface FacesWebSocket


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;
    }
}


Enter fullscreen mode Exit fullscreen mode

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.

 <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>

Enter fullscreen mode Exit fullscreen mode

Añada componentes Websocket, dentro de un componente


 <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"/>


Enter fullscreen mode Exit fullscreen mode

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

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

onerror="errorListener"/>

Enter fullscreen mode Exit fullscreen mode

Edite la Clase Dashboard.java

Agregue los atributos para manejar el WebSocket

// <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>  

Enter fullscreen mode Exit fullscreen mode
  • Leer la cantidad de segundos mínimos establecido en el archivo microprofile-config.properties.

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

En el método init()

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

...
}

Enter fullscreen mode Exit fullscreen mode

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()

public String prepareNew() {
    dialogVisibleWebSocket = Boolean.TRUE;
    dialogVisibleAddWebSocket = Boolean.TRUE;
}
Enter fullscreen mode Exit fullscreen mode

El los demás métodos prepare no se utiliza dialogVisibleAddWebSocket

public String prepareEditar(Proyecto proyecto) {
        try {

            dialogVisibleWebSocket = Boolean.TRUE;
}
}
Enter fullscreen mode Exit fullscreen mode

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()


public String refresh() {

     contadorNotificacionesWebsocket = 0;
     dialogVisibleWebSocket = Boolean.FALSE;
    dialogVisibleAddWebSocket = Boolean.FALSE;
}
Enter fullscreen mode Exit fullscreen mode

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.

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);

}



Enter fullscreen mode Exit fullscreen mode

Defina el método sendMensajesWebsocket(MessageWebSocket messageWebSocket)

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


 @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());
        }
    }
Enter fullscreen mode Exit fullscreen mode

Agregue los métodos para procesar los Websocket

@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 "";
    }


Enter fullscreen mode Exit fullscreen mode

Defina el método onCompleteWebSocket()



    public void onCompleteWebSocket() {
        try {

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

    }

Enter fullscreen mode Exit fullscreen mode

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.

 <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>

Enter fullscreen mode Exit fullscreen mode

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

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

<p:growl id="growl"/>

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

                             />

Enter fullscreen mode Exit fullscreen mode

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

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

Edite el formulario tablero.xhtml

Añada un remoteCommand


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

Enter fullscreen mode Exit fullscreen mode

Edite el profile del tablero


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

<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>

Enter fullscreen mode Exit fullscreen mode

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.

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

Añada las propiedades


   // <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>    
Enter fullscreen mode Exit fullscreen mode

En el método init()


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

Enter fullscreen mode Exit fullscreen mode

En el método refresh

   public String refresh() {
        try {
            contadorNotificacionesWebsocket = 0;
            dialogVisibleWebSocket = Boolean.FALSE;
            dialogVisibleAddWebSocket = Boolean.FALSE;
            ...
       }
}
Enter fullscreen mode Exit fullscreen mode

En los métodos prepareNew añada dialogVisibleAddWebSocket

 public String prepareNew() {

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

Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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.

 @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 "";
    }
Enter fullscreen mode Exit fullscreen mode

Agregar el método updateForRemoteWebsocket()


 public String updateForRemoteWebsocket() {
        try {
           if (dialogVisibleAddWebSocket) {

closeOverlayPanel("PF('overlayPanelTarjetaAgregar').show()");
          }

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

Enter fullscreen mode Exit fullscreen mode

Añada el método onCompleteWebSocket()

public void onCompleteWebSocket() {
        try {

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

    }

Enter fullscreen mode Exit fullscreen mode

Defina el método para validar cambios en el Proyecto

 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 "";
    }

Enter fullscreen mode Exit fullscreen mode

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.

 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);
}
Enter fullscreen mode Exit fullscreen mode

Añada el método


@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());
        }
    }

Enter fullscreen mode Exit fullscreen mode

En el dialogo que usamos para agregar imágenes

Image description

Invocar los métodos

  <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"/>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)