Precisa de esclarecimento sobre configuração de bean / recurso do JMS vs ActiveMQ

Parece haver alguma inconsistência sobre como usar os resources do JMS e configurar o activationConfig com o @ActivationConfigProperty adequado em uma anotação @MessageDriven .

Primeiro, aqui está minha configuração de recurso ( glassfish-resources.xml , mas traduzível para outros descritores de implantação). Isso é aplicado ao Glassfish ( asadmin add-resources glassfish-resources.xml ) junto com o ActiveMQ Resource Adapter :

           MyApp JMS Queue     MyApp Connection Factory     

Aqui está o meu bean de provedor de mensagens. Você notará que os nomes JNDI foram localizados e os resources do ActiveMQ serão usados ​​sem erro, a mensagem enviada para a fila correta:

 @Stateless @LocalBean public class ServicesHandlerBean { @Resource(mappedName = "jms/queue/myApp") private Queue queue; @Resource(mappedName = "jms/factory/myApp") private ConnectionFactory factory; public void sendJMSMessage(MessageConfig messageData) throws JMSException { Connection connection = null; Session session = null; try { connection = factory.createConnection(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(queue); messageProducer.send(createJMSMessage(session, messageData)); } finally { if (session != null) { try { session.close(); } catch (JMSException e) { Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot close session", e); } } if (connection != null) { connection.close(); } } } } 

A confusão começa ao definir um bean @MessageDriven . O seguinte, que usa mappedName, gera uma exceção:

 @MessageDriven(mappedName = "jms/queue/myApp") public class MessageBean implements MessageListener 

Aviso: RAR8000: O método setName não está presente na class: org.apache.activemq.command.ActiveMQQueue Aviso: RAR7097: Nenhum método setter presente para a propriedade Name na class org.apache.activemq.command.ActiveMQQueue Info: visitando Referências não visitadas Informações: visitando referências não visitadas Aviso: RAR8501: Exceção durante a ativação do ponto final para ra [activemq-rar], activationSpecClass [org.apache.activemq.ra.ActiveMQActivationSpec]: javax.resource.ResourceException: Tipo de destino desconhecido: null Grave: MDB00017 : [InvoiceProductionMessageBean]: Exceção ao criar o contêiner de bean acionado por mensagens: [java.lang.Exception] Grave: java.lang.Exception

Sou forçado a definir meu MDB como tal:

 @MessageDriven( activationConfig = { @ActivationConfigProperty(propertyName = "connectionFactoryLookup", propertyValue = "jms/factory/myApp"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "myAppAMQ"), @ActivationConfigProperty(propertyName = "messageSelector", propertyValue = " JMSType = 'TypeA' "), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") } ) public class MessageBean implements MessageListener 

E eu preciso fornecer um glassfish-ejb-jar.xml dizendo ao container para usar o recurso ActiveMQ, caso contrário eu recebo um java.lang.ClassCastException :

Aviso: RAR8501: Exceção durante a ativação do ponto final para ra [jmsra], activationSpecClass [com.sun.messaging.jms.ra.ActivationSpec]: java.lang.ClassCastException: org.apache.activemq.ra.ActiveMQConnectionFactory não pode ser convertido para com. sun.messaging.jms.ra.DirectConnectionFactory Grave: MDB00017: [MessageBean]: Exceção ao criar o contêiner de bean acionado por mensagens: [java.lang.Exception] Grave: java.lang.Exception

glassfish-ejb-jar.xml

      MessageBean  activemq-rar     

Portanto, parece haver algumas inconsistências entre como um produtor pode usar um recurso (JNDI) e como um consumidor o faz (XML + @ActivationConfigProperty ). Além disso, as propriedades EE7 ActivationConfigProperty não parecem funcionar. Por exemplo, usar destinationLookup não pesquisa o destino e sou forçado a usar a propriedade de destination do ActiveMQ.

O ActiveMQ lista as seguintes propriedades de especificação de ativação :

acknowledgeMode (O modo de Confirmação do JMS a ser usado. Os valores válidos são: Confirmação automática ou Dupply-ok-acknowledledge)

clientId (O ID do Cliente JMS a ser usado (necessário apenas para tópicos duráveis))

destinationType (O tipo de destino; uma fila ou tópico)

destino (o nome do destino (nome da fila ou do tópico))

enableBatch (Usado para ativar o lote de transactions para aumentar o desempenho)

maxMessagesPerBatch (O número de mensagens por lote de transação)

maxMessagesPerSessions (Esse é, na verdade, o tamanho da pré-busca para a assinatura. (Sim, mal nomeado).)

maxSessions (O número máximo de sessões simultâneas a serem usadas)

messageSelector (O Seletor de Mensagens JMS para usar na assinatura para executar o roteamento baseado em conteúdo que filtra as mensagens)

noLocal (necessário apenas para assinaturas de tópicos; indica se as mensagens publicadas localmente devem ser incluídas na assinatura ou não)

password (A senha para a conexão JMS)

subscriptionDurability (se uma assinatura durável (tópico) é necessária ou não. Os valores válidos são: Durable ou NonDurable)

subscriptionName (O nome do assinante durável. Usado somente para tópicos duráveis ​​e combinado com o clientID para identificar exclusivamente a assinatura de tópico durável)

userName (O usuário da conexão JMS)

useRAManagedTransaction (Normalmente, um adaptador de resources entrega mensagens a um nó de extremidade gerenciado por um contêiner. Normalmente, esse contêiner gosta de ser aquele que deseja controlar a transação na qual a mensagem de input está sendo entregue. Mas, às vezes, você deseja entregar para um sistema de contêiner mais simples que não controlará a transação de input.Nesses casos, se você definir useRAManagedTransaction como true, o adaptador de resources confirmará a transação se nenhuma exceção foi gerada a partir do MessageListener e da reversão se uma exceção for lançada.)

initialRedeliveryDelay (O atraso antes de novas entregas começarem. Também configurável no ResourceAdapter)

maximumRedeliveries (O número máximo de novas entregas ou -1 para nenhum máximo. Também configurável no ResourceAdapter)

redeliveryBackOffMultiplier (O multiplicador a ser usado se o recuo exponencial estiver habilitado. Também configurável no ResourceAdapter)

redeliveryUseExponentialBackOff (para ativar o backoff exponencial. Também configurável no ResourceAdapter useJndi no false quando true, use destination como um nome jndi)

A especificação Java EE7 lista as seguintes Propriedades de especificação de ativação :

acknowledgeMode (Essa propriedade é usada para especificar o modo de confirmação JMS para a entrega de mensagens quando a demarcação de transação gerenciada por bean é utilizada. Seus valores são Auto_knownowledge ou Dups_ok_acknowledge. Se essa propriedade não for especificada, as semânticas JMS AUTO_ACKNOWLEDGE serão assumidas.

messageSelector (Essa propriedade é usada para especificar o seletor de mensagens JMS a ser utilizado na determinação de quais mensagens um bean acionado por mensagens JMS deve receber)

destinationType (Essa propriedade é usada para especificar se o bean acionado por mensagens deve ser usado com uma fila ou um tópico. O valor deve ser javax.jms.Queue ou javax.jms.Topic.)

destinationLookup (Essa propriedade é utilizada para especificar a fila ou o tópico do JMS a partir do qual um bean acionado por mensagens do JMS deve receber mensagens.)

connectionFactoryLookup (Essa propriedade é utilizada para especificar o connection factory JMS que será utilizado para conectar-se ao provedor JMS a partir do qual um bean acionado por mensagens JMS deve receber mensagens.)

subscriptionDurability (Se o bean acionado por mensagens tiver a intenção de ser usado com um tópico, essa propriedade poderá ser usada para indicar se uma assinatura durável ou não durável deve ser usada. O valor dessa propriedade deve ser Durable ou NonDurable)

subscriptionName (Esta propriedade é utilizada para especificar o nome da assinatura durável, se o bean acionado por mensagens tiver a intenção de ser usado com um Tópico, e o provedor de beans tiver indicado que uma assinatura durável deve ser usada.)

clientId (Esta propriedade é utilizada para especificar o identificador de cliente JMS que será utilizado ao conectar-se ao provedor JMS a partir do qual um bean acionado por mensagens JMS receberá mensagens. Se essa propriedade não for especificada, o identificador de cliente será deixado não configurado. )

Qual é a maneira correta de usar um recurso do ActiveMQ em um produtor e consumidor com apenas @Inject points e lookup jndi? Eu gostaria de evitar o glassfish-ejb-jar.xml e definir o nome da fila com um @ActivationConfigProperty .

Sim, cada servidor de aplicativos faz as coisas de maneira um pouco diferente. Mais importante, eles fazem isso de maneira diferente, não em como você o configura – essa parte é simples, mas no comportamento do tempo de execução quando você espera do servidor JMS um SLA, como processamento de mensagens ordenadas – mesmo em caso de falha.

Por exemplo, se você tiver um processo crítico de negócios, onde a Mensagem 2 só poderá ser processada após a Mensagem 1. E sua mensagem 1 falhar e você quiser ser tentada novamente, mas você também configurou um atraso de devolução de 200 ms. Alguns servidores de aplicativos, por padrão, pensam: a mensagem 1 falhou, eu tentei novamente em 200 ms, saltei para a próxima mensagem … E o processo de negócios está morto porque sua expectativa de consumo de mensagens ordenadas foi apenas violada.

Normalmente, bons servidores JMS oferecem a capacidade de configurá-lo de tal forma que você possa atender ao seu SLA requerido … mas é complicado.

Como regra, você deve configurar em seu MDB por meio de annotations, qualquer propriedade que esteja trabalhando de forma cruzada entre vários servidores de aplicativos. Normalmente, a nomenclatura JNDI pode funcionar – mas é difícil, porque o JNDI é altamente dependente do contêiner. Propriedades como: – Proptionu de ativação: destinationType = javax.jms.Topic

Isso é bastante normal, então você pode simplesmente colocá-lo por meio de annotations.

Mas quando você chega aos aspectos complicados, como especificar o connection factory para se conectar ao destino. Ou você deve permitir que o servidor JMS leia em lote N mensagens de uma só vez, ou você quer forçá-lo uma por vez, etc … Isso é altamente dependente do seu contêiner, e você vai querer configurar isso não através de anotação pelo descritor de implementação ejb.

Por exemplo, no weblogic, você desejaria usar: weblogic-ejb-jar.xml Para ajustar coisas como o nome JNDI para acessar a fila, max-beans-in-free-pool etc …

No Wildfly, onde o ActiveMQ é usado, você deve ot usar o: jboss-ejb3.xml

Descritor de implantação para fazer isso.

Assim, através de annotations – você deve ter o denominador comum de metadados equivalentes a vários contêineres. No descritor de implantação, você enriquece a configuração com os metadados ausentes.

Os bons servidores de aplicativos sempre estarão executando um processo de mesclagem no qual combinam os Metadados no MDB com os metadados no descritor de implantação. E quando há uma colisão, eles assumem a configuração no descritor de implantação.

E assim por diante.

Então, algumas coisas, você realmente precisa ajustar o contêiner atrapalhado no descritor de implantação suportado pelo contêiner. Em seu código java, você deve manter apenas os metadados que são compatíveis de forma cruzada.

E, finalmente, obter o comportamento exato do JMS para tratamento de mensagens em diferentes servidores de aplicativos que usam diferentes implementações de servidor JMS … é bastante complicado.

A menos que você tenha o scneario básico que não se importa com o processamento ordenado, você tem vários MDBs sendo executados em paralelo em uma fila porque não acontece antes do relacionamento … então é trivial fazer com que uma configuração slopy funcione.

Parece que todos os servidores fazem isso de forma um pouco diferente.