Versiones comparadas

Clave

  • Se ha añadido esta línea.
  • Se ha eliminado esta línea.
  • El formato se ha cambiado.

...

Esta sería la única forma en caso de que desee enviar de forma asincrónica un mensaje a una conexión push con ámbito de view o session, y/o desee pasar algo desde FacesContext o de ámbito EVENT/PAGE/SESSION como argumento (con modificador final).

Sugerencias de Diseño

...

de la UI

Si se quieren realizar actualizaciones complejas de la UI, la forma más sencilla es usando <f:ajax> dentro de <fw:socket>. Ejemplo:

Bloque de código
languagexml
<h:panelGroup id="foo">
    ... (some complex UI here) ...
</h:panelGroup>

<h:form>
    <fw:socket channel="someChannel" scope="view">
Bloque de código
languagexml
<jms:managed-topic-publisher name="pushPublisher" 
        <f:ajax event="someEvent" listener="#{bean.pushed}"   render=":foo" />
    </fw:socket>
</h:form>


Donde el mensaje push, solo contien el nombre del evento Ajax. Puedes usar cualquier nombre de evento personalizado.

Bloque de código
languagejava
someChannel.send("someEvent");


Otra alternativa, es combinar <fw:socket> con <p:remoteCommand>, ejemplo:

Bloque de código
languagexml
<h:panelGroup id="foo">
    ... (some complex UI    auto-create="true" installDependencies="es.um.atica.faces.push.pushInitialization"
                             topic-jndi-name="jms/pruebaPushTopic"/>
<jms:topic-connection topic-connection-factory-jndi-name="jms/pruebaPushConnectionFactory"
	                             installDependencies="es.um.atica.faces.push.pushInitialization"/>

Ahora se puede inyectar el componente pruebaPushPublisher en cualquier componente:

here) ...
</h:panelGroup>

<fw:socket channel="someChannel" scope="view" onmessage="someCommandScript" />
<h:form>
    <p:remoteCommand name="someCommandScript" action="#{bean.pushed}" update=":foo" />
</h:form>


Si se pasa un Map<String, V> o un POJO (JAVABEAN) como el objeto en el mensaje push, después todas las entradas/propiedades estarán disponibles como parámetros de la solicitud en la acción del comando #{bean.pushed}.

 

Sugerencias de Diseño en Cluster

En el caso que la aplicación este desplegada en un cluster con varios nodos, y el evento push se lanza desde un nodo diferente al que esta conectado el cliente (Navegador Web), entonces este no será notificado. Una solución es usar temas (topic) JMS, lanzar el evento push a través de JMS, y usar MDB (message driven bean) para reenviar el evento push a JBoss Seam en cada nodo.


Con JBoss Seam tenemos varios componentes para ayudar a trabajar con JMS. Ejemplo para la configuración de JMS con JBoss Seam, en el fichero components.xml registramos el manager del tema (topic) JMS, como norma general, el nombre del tema  comenzara con el nombre de la aplicación en minúsculas seguido de PushTopic, en el ejemplo usaremos pruebaPushTopic. Para el nombre del publisher, podemos usar el nombre que queramos. Veamos un ejemplo de configuración:

Bloque de código
languagexml
<jms:managed-topic-publisher name="pushPublisher" 
Bloque de código
languagejava
@Name("pushChangeNotifier")
public class PushManager {

   @In
   private TopicPublisher pruebaPushPublisher;   

   @In
   private TopicSession topicSession;

   public void fireEvent(PushEvent event) {
        try {
           pruebaPushPublisher.publish(topicSession.createObjectMessage(event.getMessage()));
        } catch (Exception ex) {
           throw     new RuntimeException(ex);
        } 
   }

}

...

Bloque de código
languagejava
package auto-create="true" installDependencies="es.um.atica.pruebafaces.push.pushInitialization"
                             topic-jndi-name="jms/pruebaPushTopic"/>
<jms:topic-connection topic-connection-factory-jndi-name="jms/pruebaPushConnectionFactory"
	                             installDependencies="es.um.atica.faces.push.pushInitialization"/>

Ahora se puede inyectar el componente pruebaPushPublisher en cualquier componente:

Bloque de código
languagejava
@Name("pushEventsManager ")
public class PushEventsManager {

   @In
   private TopicPublisher pushPublisher;   

   @In
   private TopicSession topicSession;

   public void fireEvent(PushEvent event) {
        try {
           pushPublisher.publish(topicSession.createObjectMessage(event.getMessage()));
        } catch (Exception ex) {;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.core.Events;
import org.jboss.seam.log.Log;
import org.jboss.seam.util.Strings;

import es.um.atica.faces.push.PushEvent;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(
        propertyName = "destinationType",
        propertyValue = "javax.jms.Topic"
    ),
    @ActivationConfigProperty(
        propertyName = "destinationLookup",
        propertyValue = "jms/pruebaPushTopic"
   throw new RuntimeException(ex);
     )
})
@Name("pushReceiver")
@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)
public class PushReceiver implements MessageListener {

	@Logger
	private Log log;

	protected void raiseEvent(String eventType, PushEvent event) {
		if (Strings.isEmpty(eventType)) {
			log.info("Raise push event '#0'", event.getClass().toString());
			Events.instance().raiseEvent(event);
		} else {
			log.info("Raise push event '#0'", eventType);
			Events.instance().raiseEvent(eventType, event);
		}
	}

	@Override
	public void onMessage(Message jmsMessage) {
		try {
			String eventType = jmsMessage.getStringProperty("eventType");
			PushEvent event = jmsMessage.getBody(PushEvent.class);
			raiseEvent(eventType, event);
		} catch (JMSException ex) {
			log.error("Problem sending push event message.", ex);
		}
	}

}

...

Bloque de código
languagejava
@In
private PushManager pushManager;

public void onSomeEntityChange(Entity entity) {
    pushManager.fireEvent(new PushEvent(entity.getSomeProperty()));
}

...

Sugerencias de Diseño de la UI

...

Bloque de código
languagexml
<h:panelGroup id="foo">
    ... (some complex UI here) ...
</h:panelGroup>

<h:form>
    <fw:socket channel="someChannel" scope="view">
        <f:ajax event="someEvent" listener="#{bean.pushed}" render=":foo" />
    </fw:socket>
</h:form>

Donde el mensaje push, solo contien el nombre del evento Ajax. Puedes usar cualquier nombre de evento personalizado.

Bloque de código
languagejava
someChannel.send("someEvent");

...

Bloque de código
languagexml
<h:panelGroup id="foo">
    ... (some complex UI here) ...
</h:panelGroup>

<fw:socket channel="someChannel" scope="view" onmessage="someCommandScript" />
<h:form>
    <p:remoteCommand name="someCommandScript" action="#{bean.pushed}" update=":foo" />
</h:form>

...

   } 
   }

}


Para recibir los mensajes JMS tenemos que configurar un MDB:

Bloque de código
languagejava
package es.um.atica.prueba.push;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.core.Events;
import org.jboss.seam.log.Log;
import org.jboss.seam.util.Strings;

import es.um.atica.faces.push.PushEvent;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(
        propertyName = "destinationType",
        propertyValue = "javax.jms.Topic"
    ),
    @ActivationConfigProperty(
        propertyName = "destinationLookup",
        propertyValue = "jms/pruebaPushTopic"
    )
})
@Name("pushReceiver")
@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)
public class PushReceiver implements MessageListener {

	@Logger
	private Log log;

	protected void raiseEvent(String eventType, PushEvent event) {
		if (Strings.isEmpty(eventType)) {
			log.info("Raise push event '#0'", event.getClass().toString());
			Events.instance().raiseEvent(event);
		} else {
			log.info("Raise push event '#0'", eventType);
			Events.instance().raiseEvent(eventType, event);
		}
	}

	@Override
	public void onMessage(Message jmsMessage) {
		try {
			String eventType = jmsMessage.getStringProperty("eventType");
			PushEvent event = jmsMessage.getBody(PushEvent.class);
			raiseEvent(eventType, event);
		} catch (JMSException ex) {
			log.error("Problem sending push event message.", ex);
		}
	}

}


Ahora, usamos PushManager#fireEvent() para lanzar eventos JMS desde un nodo del cluster, que permite después lanzar los eventos de JBoss Seam en todos los nodos el cluster y asi enviar el mensaje al canal push correspondiente:

Bloque de código
languagejava
@In
private PushEventsManager pushEventsManager;

public void onSomeEntityChange(Entity entity) {
    pushEventsManager.fireEvent(new PushEvent(entity.getSomeProperty()));
}


Para FundeWeb hemos añadido un sistema a partir del Arquetipo versión 2.0.11, que añade un perfil llamada cluster que permite terne fácilmente esta funcionalidad. Solo tendréis que usar el PushEventsManager para enviar los mensajes a través de las conexiones push. Para añadir esa configuración a una aplicación existente hay que descargar este push-cluster.zip y descomprimir con Extraer aquí en el modulo Web.

Más información sobre JMS:

...

  • .



Configuraciones Adicionales Obligatorias

...