Статьи

Извините, автономные примеры JMS 2.0 не так просты

Что ж, мне придется оставить это для кого-то, кто более опытен, чем я, потому что сейчас у меня больше дел. У меня был вопрос от читателя об исходном коде  Java EE 7 Developer Handbook . Работает ли JMS 2.0 как клиент? Из этого я понял, что разработчик должен иметь возможность запускать GlassFish 4 и получать доступ к очередям и темам JMS из отдельного контейнера. Раньше я делал это для инвестиционных банков, использующих J2EE 1.4 с серверами приложений WebSphere и WebLogic, и поэтому я уверен, что код, с которым я работал несколько месяцев, обязательно сработает, вопрос читателя — скорее мой собственный надзор. Должно быть, я мечтал об этих результатах.

Извините, я потратил несколько дней на то, чтобы заставить GlassFish Embedded 4.0 и затем управляемый сервер работать вместе, чтобы продемонстрировать простой пример JMS 2.0. Я также использовал тест Arquillian для работы, а затем также попробовал не-Arquillian версию, и теперь я очень обеспокоен. Казалось бы, никто не знает в Сети, как точно настроить автономную JMS 2.0 как приложение Java SE.

Это насколько я понимаю:

package je7hb.jms.essentials;

import static org.junit.Assert.*;

import javax.annotation.Resource;
import javax.ejb.*;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.*;
import org.junit.runner.RunWith;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

@RunWith(Arquillian.class)
public class AsynchronousJMSMessageArquillianTest {

    @Deployment
    public static WebArchive createDeployment() {
        WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "asyncjms.war")
                .addClasses(PayloadCheck.class)
                .addAsWebInfResource(
                        new File("src/test/resources-glassfish-managed/glassfish-resources.xml"),
                        "glassfish-resources.xml")
                .addAsWebInfResource(
                        EmptyAsset.INSTANCE, "beans.xml");

        System.out.println(webArchive.toString(true));
        return webArchive;
    }

    private List<String> messages = new ArrayList<>();

    private CompletionListener completionListener = new CompletionListener() {
        @Override
        public void onCompletion(Message msg) {
            TextMessage textMsg = (TextMessage)msg;
            try {
                System.out.printf("%s.onCompletion(%s) Thread: %s\n",
                        getClass().getSimpleName(), textMsg.getText(), Thread.currentThread());
            } catch (JMSException e) {
                e.printStackTrace(System.err);
            }
        }

        @Override
        public void onException(Message msg, Exception ex) {
            ex.printStackTrace(System.err);
        }
    };

    @Test
    @RunAsClient
    public void shouldFire() throws JMSException, InterruptedException, NamingException {
        Properties properties = new Properties();
//        properties.put("com.sun.appserv.iiop.endpoints", "localhost:7676");
//        properties.put("org.omg.CORBA.ORBInitialHost", "localhost");
//        properties.put("org.omg.CORBA.ORBInitialPort", "3700");
//        properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory");
//        properties.put(Context.PROVIDER_URL, "mq://localhost:7676/");
//        properties.put("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
//        properties.put("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");

        properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory");
        properties.put(Context.URL_PKG_PREFIXES, "com.sun.enterprise.naming");
        properties.put(Context.STATE_FACTORIES, "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
//        properties.put("org.omg.CORBA.ORBInitialHost", "localhost");
//        properties.put("org.omg.CORBA.ORBInitialPort", "3700");
        properties.put(Context.PROVIDER_URL, "mq://localhost:7676"); // vm://localhost:
//        properties.put(Context.PROVIDER_URL, "iiop://localhost:7676"); // vm://localhost:

        InitialContext jndiContext = new InitialContext(properties);
        System.out.printf("\t jndiContext=%s\n", jndiContext);

        ConnectionFactory connectionFactory =
                (ConnectionFactory)jndiContext.lookup("jms/demoConnectionFactory");

        Queue queue = (Queue)jndiContext.lookup("jms/demoQueue");

        assertNotNull(connectionFactory);
        assertNotNull(queue);

        Connection connection = connectionFactory.createConnection();
        JMSContext context = connectionFactory.createContext(
                Session.AUTO_ACKNOWLEDGE );
        JMSProducer producer = context.createProducer();

        messages.clear();

        producer.setAsync(completionListener);

        producer.send(queue, "hello");
        producer.send(queue, "world");
        producer.send(queue, "asynchronously");

        Thread.sleep(2000); // Delay before shutdown

        System.out.println("Done");

    }
}

Доказательства есть в коде, и вы можете просмотреть закомментированные строки, потому что каждый, а некоторые из них были опробованы

Причина в том, что определенные вызовы, такие как  setAsync , запрещены в Java EE и веб-контейнере. Поэтому, если вам нужен асинхронный JMS-ридер или писатель в EE, вы можете забыть об этом.

Файл конфигурации ресурса Glassfish:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
    <admin-object-resource enabled="true" jndi-name="jms/demoQueue" object-type="user" res-adapter="jmsra" res-type="javax.jms.Queue">
        <description/>
        <property name="Name" value="PhysicalQueue"/>
    </admin-object-resource>
    <admin-object-resource enabled="true" jndi-name="jms/demoTopic" object-type="user" res-adapter="jmsra" res-type="javax.jms.Topic">
        <description/>
        <property name="Name" value="PhysicalTopic"/>
    </admin-object-resource>
    <connector-connection-pool name="jms/demoDestinationFactoryPool"
                               connection-definition-name="javax.jms.QueueConnectionFactory"
                               resource-adapter-name="jmsra" />
    <connector-resource enabled="true"
                        jndi-name="jms/demoConnectionFactory"
                        pool-name="jms/demoDestinationFactoryPool"
                        object-type="system-all" />
</resources>

На самом деле, это такое неловкое положение, я бы рекомендовал не смотреть на GlassFish для отдельного клиента JMS, а, возможно, подождать  WildFly  или  Tom EE . Очевидно, что использование JMS 2.0  вне  контейнера EE очень непривлекательно, и я думаю, что это будет чертовски легко для новых разработчиков, в противном случае они хотят коснуться Java EE с помощью штанги баржи. Если технический эксперт не может разработать простой пример использования JMS вне контейнера, то какой в ​​этом смысл?  Официальная страница JMS 2.0  также бесполезна.

Частично проблема заключается в том, что нет стандартного JMS API на стороне клиента для Java SE, JAX RS 2.0 теперь имеет клиентский API. Другое заключается в том, что некоторые из артефактов GlassFish Maven имеют огромные зависимости, которые вызывают загрузку всех модулей сервера приложений (я указал пальцем на это  gf-client:4.0-SNAPSHOT). Наконец, эта трудная проблема на самом деле не задокументирована, и, к сожалению, пример учебного кода по Java EE 7 доступен только как часть пакета, а не как независимый для быстрого доступа.

Поэтому я планирую покинуть этот  JMS async проект как часть распространения кода книги. Извините, что код не работает вообще. Этот код должен работать как единый интеграционный тест в ссылочной реализации GlassFish и Arquillian. У меня больше нет времени на это, но я исключу его создание (потому что тест не пройден) в новых усовершенствованиях подпроекта Gradle, которые я сейчас пишу.

: - | + PP +