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>
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)
jar
file, then the @ManagedServices
are recognized. Note: You may need to copy static files (js and html) in your pom.xml
before packaging.