javamavenspring-bootatmosphereembedded-tomcat-8

org.atmosphere.cpr.AtmosphereMappingException in Spring Boot executable war file


I have a Spring Boot Application into which I have integrated the Atmosphere Framework.

This is the Application.java file:

package com.myproject.something;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.core.env.SimpleCommandLinePropertySource;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        addDefaultProfile(app, new SimpleCommandLinePropertySource(args));
        app.run(args);
    }

    private static void addDefaultProfile(SpringApplication app, SimpleCommandLinePropertySource source) {
        if (!source.containsProperty("spring.profiles.active")
                && !System.getenv().containsKey("SPRING_PROFILES_ACTIVE")) {
            app.setAdditionalProfiles("dev");
        }
    }
}

In order to have the Atmosphere Framework integrated into my Spring Boot Application, I have loosely been following this resource combining elements from the official Atmosphere Documentation.

I ended up having the classes MyAtmosphereConfig.java and MyAtmosphereManagedService.java:

package com.myproject.something.atmosphere;
import java.util.Collections;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.atmosphere.cpr.AtmosphereServlet;
import org.atmosphere.cpr.ContainerInitializer;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

@Configuration
public class MyAtmosphereConfig {
    @Bean
    public EmbeddedAtmosphereInitializer atmosphereInitializer() {
        return new EmbeddedAtmosphereInitializer();
    }

    @Bean
    public ServletRegistrationBean atmosphereServlet() {
        ServletRegistrationBean registration = new ServletRegistrationBean(new AtmosphereServlet(), "/myAtmosphereUrl");
        registration.addInitParameter("org.atmosphere.cpr.packages", "com.myproject.something.atmosphere");
        registration.setLoadOnStartup(1);
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registration;
    }

    private static class EmbeddedAtmosphereInitializer extends ContainerInitializer
            implements ServletContextInitializer {
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            onStartup(Collections.<Class<?>>emptySet(), servletContext);
        }
    }
}

package com.myproject.something.atmosphere;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Inject;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceFactory;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ManagedService(path = "/myAtmosphereUrl")
public class MyAtmosphereManagedService {

    private final Logger log = LoggerFactory.getLogger(MyAtmosphereManagedService.class);

    @Inject
    public static AtmosphereResourceFactory factory;

    @Inject
    public AtmosphereResource resource;

    @Ready
    public void onReady() throws JSONException {
        String atmosJSessionId = resource.getRequest().getRequestedSessionId();
        log.info("Initiating Atmosphere client connection with jsession = " + atmosJSessionId);
    }

    @Disconnect
    public void onDisconnect() {
        String atmosJSessionId = resource.getRequest().getRequestedSessionId();
        log.info("Unregistering Atmosphere client connection with jsession = " + atmosJSessionId);  
    }
}

I have been developing inside the Eclipse IDE, executing my Spring Boot Application as a Java Application. (Right-Click on Application.java -> Run As -> Java Application)

Running it as described in the above fashion -as a Java Application from within the Eclipse IDE- the Front-End part of my Application can successfully access the URL exposed by the Atmosphere Framework like so:

(function() {
  'use strict';
  setTimeout(function() {
    // Setup Atmosphere client
    var myAtmosphereUrl = 'http://localhost:8080/myAtmosphereUrl';
    var socket = atmosphere;
    var subSocket;

    var transport = 'sse';
    var request = {
      url : myAtmosphereUrl,
      contentType : "application/json",
      logLevel : 'debug',
      transport : transport,
      trackMessageLength : true,
      reconnectInterval : 5000
    };
      request.onOpen = function(response) {
      transport = response.transport;
      request.uuid = response.request.uuid;
      console.log("onOpen called!");
    };
      request.onMessage = function(response) {
      transport = response.transport;
      request.uuid = response.request.uuid;
      console.log("onMessage called!");
    };
    subSocket = socket.subscribe(request);
  }, 1000);
})();

On the Frontend-side in the Browser console I see HTTP 200 status code and on the Backend side in the Eclipse IDE console, it is giving me the log message Initiating Atmosphere client connection with jsession = A4A8790C27D531FE7B2CD82B5B9C9447.

Unfortunately, after packaging my project into an executable war file and deploying it as a Service on a Debian Server, it does not work anymore leaving me with an error message in the log on the backend-side:

2017-10-16 10:51:11.143 ERROR 21296 --- [http-nio-8080-exec-2] o.atmosphere.cpr.AsynchronousProcessor   : No AtmosphereHandler found. Make sure you define it inside WEB-INF/atmosphere.xml or annotate using @___Service
2017-10-16 10:51:11.146 ERROR 21296 --- [http-nio-8080-exec-2] o.a.c.c.C.[.[.[/].[atmosphereServlet]    : Servlet.service() for servlet [atmosphereServlet] in context with path [] threw exception

org.atmosphere.cpr.AtmosphereMappingException: No AtmosphereHandler found. Make sure you define it inside WEB-INF/atmosphere.xml or annotate using @___Service
  at org.atmosphere.cpr.AsynchronousProcessor.action(AsynchronousProcessor.java:147) ~[atmosphere-runtime-2.4.5.jar!/:2.4.5]
  at org.atmosphere.cpr.AsynchronousProcessor.suspended(AsynchronousProcessor.java:115) ~[atmosphere-runtime-2.4.5.jar!/:2.4.5]
  at org.atmosphere.container.Servlet30CometSupport.service(Servlet30CometSupport.java:68) ~[atmosphere-runtime-2.4.5.jar!/:2.4.5]
  at org.atmosphere.cpr.AtmosphereFramework.doCometSupport(AtmosphereFramework.java:2287) ~[atmosphere-runtime-2.4.5.jar!/:2.4.5]
  at org.atmosphere.cpr.AtmosphereServlet.doPost(AtmosphereServlet.java:191) ~[atmosphere-runtime-2.4.5.jar!/:2.4.5]
  at org.atmosphere.cpr.AtmosphereServlet.doGet(AtmosphereServlet.java:177) ~[atmosphere-runtime-2.4.5.jar!/:2.4.5]
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) ~[spring-boot-1.4.1.RELEASE.jar!/:1.4.1.RELEASE]
  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:105) ~[spring-boot-actuator-1.4.1.RELEASE.jar!/:1.4.1.RELEASE]
  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  ...
  at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:107) ~[spring-boot-actuator-1.4.1.RELEASE.jar!/:1.4.1.RELEASE]
  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar!/:4.3.3.RELEASE]
  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:677) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_111]
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_111]
  at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.5.jar!/:8.5.5]
  at java.lang.Thread.run(Thread.java:745) [na:1.8.0_111]

The Browser console gives me a HTTP 500 - Internal Server Error status code.

The creation of the executable war file is triggered by the Maven command maven package and is configured in the pom.xml in this fashion:

<?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.myproject</groupId>
    <artifactId>something</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>MySomething</name>
    <description>My Something Description</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
    </parent>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>build-info</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <executable>true</executable>
        </configuration>
      </plugin>
      ...
    </plugins>
  </build>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-rest-core</artifactId>
            <version>2.6.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</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-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.atmosphere</groupId>
            <artifactId>atmosphere-runtime</artifactId>
            <version>2.4.5</version>
        </dependency>
        <dependency>
            <groupId>org.atmosphere</groupId>
            <artifactId>atmosphere-spring</artifactId>
            <version>2.4.4</version>
        </dependency>
    </dependencies>
</project>

I have already tried placing an atmosphere.xml file into the WEB-INF folder of the war file, but it still seems that the Servlet annotated with @ManagedService is not picked up.

This was the content of the atmosphere.xml, which did not solve the error:

<atmosphere-handlers>
    <atmosphere-handler support-session="true"
                        context-root="/"
                        broadcaster="org.atmosphere.cpr.DefaultBroadcaster"
                        class-name="com.myproject.something.atmosphere.MyAtmosphereManagedService">
    </atmosphere-handler>
</atmosphere-handlers>

Can anybody tell me whats wrong with my configuration or is this probably some bug related to the minor versions of the embedded Tomcat, Atmosphere and Spring Boot?


EDIT:

It is possible to reproduce this error by cloning the official Atmosphere-Spring-Boot Sample project and changing the pom.xml to package the project to a war file:

<?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>
    <parent>
        <groupId>org.atmosphere.samples</groupId>
        <artifactId>atmosphere-samples</artifactId>
        <version>2.5.0-SNAPSHOT</version>
    </parent>
    <artifactId>spring-boot-sample-atmosphere</artifactId>
    <name>Spring Boot Atmosphere Sample</name>
    <description>Spring Boot Atmosphere Sample</description>
    <url>http://projects.spring.io/spring-boot/</url>
  <packaging>war</packaging>

    <organization>
        <name>Pivotal Software, Inc.</name>
        <url>http://www.spring.io</url>
    </organization>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${springboot-version}</version>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <dependency>
            <groupId>org.atmosphere</groupId>
            <artifactId>atmosphere-runtime</artifactId>
            <version>${atmosphere-version}</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>atmosphere-javascript</artifactId>
            <version>2.2.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${springboot-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>${spring-version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${springboot-version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Solution

  • I found two fixes for this.

    First fix:
    1. Changed the Spring Boot Version in pom.xml to:

    <parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>1.5.7.RELEASE</version>
    </parent>
    

    2. Done!

    Second fix: (if you can't change the version)

    1. Package your project into a jar file, then the @ManagedServices are recognized.

    Note: You may need to copy static files (js and html) in your pom.xml before packaging.