Атрибут ServerContainer не найден в ServletContext - PullRequest
0 голосов
/ 09 мая 2019

В моем загрузочном приложении Spring я также использую веб-сокеты. Все работает нормально, как и ожидалось в производстве.

Теперь я начал создавать UnitTests с помощью Spring-Boot-Test.

Каждый раз, когда я запускаю @SpringBootTest, я получаю следующее исключение (сокращенное):

java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'createWebSocketContainer' defined in class path resource [com/package/spring/ws/MyWebsocketConfig.class]: Invocation of init method failed;
Caused by: java.lang.IllegalStateException: Attribute 'javax.websocket.server.ServerContainer' not found in ServletContext

Длинная версия

java.lang.IllegalStateException: Failed to load ApplicationContext

at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'createWebSocketContainer' defined in class path resource [com/package/spring/ws/MyWebsocketConfig.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Attribute 'javax.websocket.server.ServerContainer' not found in ServletContext
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:830)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 24 more

Caused by: java.lang.IllegalStateException: Attribute 'javax.websocket.server.ServerContainer' not found in ServletContext
at org.springframework.util.Assert.state(Assert.java:73)
at org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean.afterPropertiesSet(ServletServerContainerFactoryBean.java:117)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
... 39 more

Spring Websocket Config Class

package com.package.spring.ws;

// removed imports

@Configuration
@EnableWebSocketMessageBroker
public class MyWebsocketConfig extends AbstractSessionWebSocketMessageBrokerConfigurer<Session> {

    private final Logger logger = LogManager.getLogger(MyWebsocketConfig.class);
    private int MAX_MESSAGE_SIZE = 1024 * 1024 * 16;

    @Override
    protected void configureStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/wss")
                .setAllowedOrigins("*")
                .withSockJS();
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {

        registration.setMessageSizeLimit(MAX_MESSAGE_SIZE);
        registration.setSendBufferSizeLimit(MAX_MESSAGE_SIZE);
        registration.setSendTimeLimit(20000);

        super.configureWebSocketTransport(registration);
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/queue", "/events", "/topic");
        config.setCacheLimit(1024 * 10);
        super.configureMessageBroker(config);
    }

    // This is breaking my Spring Unit Tests.
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
            ServletServerContainerFactoryBean container = new 
  ServletServerContainerFactoryBean();
            container.setMaxTextMessageBufferSize(MAX_MESSAGE_SIZE);
            container.setMaxBinaryMessageBufferSize(MAX_MESSAGE_SIZE);
            return container;
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.package</groupId>
    <artifactId>MyProject</artifactId>
    <version>0.8.4-RELEASE</version>
    <packaging>war</packaging>

    <name>My Project</name>
    <description>MyProject Server</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Problem exists, with and without this websocket dependency -->
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-messaging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Класс применения пружины

package com.packge.spring;
// removed imports

@ComponentScan({"com.package.spring", "com.packe.spring.security", "com.package.endpoints"})
@EnableJpaRepositories(basePackages = "com.package.db")
@EntityScan(value = "com.package.db")
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Value("my.session.cookie-name")
    private String cookieName = ApplicationConfig.SESSION_COOKIE_NAME;

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);

    }
}

Типичный тест на провал

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class LedgerTreeMovementsTest {

    @Test
    public void test0_testXYZ() {
    }
}

Задача

Проблема в том, что настоящий Тест никогда не приходит к методам тестирования, его ломают при настройке Тестового приложения. У кого-нибудь есть идея, что происходит? Нужна дополнительная информация? Огромное спасибо!

1 Ответ

0 голосов
/ 10 мая 2019

Спасибо GSSwain, который привел меня в правильном направлении, теперь я смог решить мою проблему.Кажется, что оно появляется, только если атрибут @SpringBootTest webEnviroment установлен на MOCK или NONE.Использование определенного или случайного порта сообщает Spring для загрузки правильного ServletContext.

Существует несколько способов обойти это, если вы не хотите загружать полный контекст для простых тестов.

Решение1

Это то, что нужно большинству людей.Добавьте дополнительную аннотацию @WebAppConfiguration к вашим тестовым классам, которая говорит Spring, чтобы загрузить соответствующий ServletContext с фиктивной записью атрибута для ServerContainer.

Решение 2

Для моих нужд это было все ещемного раздувать фон, вещи, которые мне не нужны, во многих тестовых случаях.Поэтому я добавил проверку в самом создании Бина и добавил свойство для включения или выключения проверки через application.properties.

В вашем классе WebsocketConfig:

        @Autowired
        private ServletContext servletContext;
        private boolean ignoreNullWsContainer;

                @Bean
        public ServletServerContainerFactoryBean createWebSocketContainer() {

            // Check if null-container is allowed to prevent Exceptions
            if (ignoreNullWsContainer) {
                // Check if attribute is set in the ServletContext
                ServerContainer serverContainer = (ServerContainer) this.servletContext.getAttribute("javax.websocket.server.ServerContainer");
                if (serverContainer == null) {
                    logger.error("Could not initialize Websocket Container in Testcase.");
                    return null;
                }
            }

            ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
            container.setMaxTextMessageBufferSize(MAX_MESSAGE_SIZE);
            container.setMaxBinaryMessageBufferSize(MAX_MESSAGE_SIZE);
            return container;
        }

        @Value("${project.ignore-null-websocket-container}")
        private void setIgnoreNullWsContainer(String flag) {
            this.ignoreNullWsContainer = Boolean.parseBoolean(flag);
        }

И в вашемapplication.properties в каталоге ресурсов Test src (по умолчанию dir src / test / resources / application.properties

project.ignoreNullWsContainer = true

Таким образом, если вы не установите это свойство в файле свойств вашей производственной среды, Spring все равно будетпотерпеть неудачу, если что-то не так с ServerContainer.

Это то, что я взял в конце, потому что мне не нужны функции теста Spring, мне нужен был только Spring, чтобы забрать мою конфигурацию источника данных (в файле свойств)и выполнение моих аннотаций @Sql: D

Надеюсь, это также поможет кому-то еще, сэкономить часы поиска, боли и поиска в сети;):)

...