Significant update below (October 15, 2014)
========== Original publication ====================
I have a notification support service using Atmosphere and Jersey. I am trying to run it on Tomcat on Linux and am facing a problem. (Note that this works fine when run in Tomcat-embedded-in-Eclipse, so this may become a Tomcat configuration problem.)
The problem is that the broadcast messages appear in the queue or are buffered and are not actually delivered to the client until I turn off Tomcat. At this moment, they are all accepted by the client immediately.
I tried several things that I found in different publications to try to solve this problem, but my efforts split because I do not know what causes the problem:
- I tried explicitly using the Tomcat NIO protocol.
- I tried to explicitly disable text and binary buffering, and I also queued enough responses to trigger a flash if buffering was the cause.
- I tried working in Tomcat 8 instead (but it had different unrelated problems.)
- I added an atmosphere and environment dependent environment, atmosphere-compat-jbossweb and atmosphere-compatibility-cat.
None of them changed the behavior of the service. And, again, it seems to work just fine when working in Eclipse.
Environment: Ubuntu 14.04 LTS. Tomcat 7.0.54. Atmosphere 2.1.5. For the client, I just use telnet at the moment.
(This code is a bit more complicated than the samples, because I have several different routes that I want to support, and I use the method suggested by this Answer, which seems to work well. I also return notification objects, not strings. This also seems to work fine. I just stripped most of the authentication elements.)
pom.xml:
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.clearcaptial</groupId> <artifactId>notifications</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>notifications Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-jersey</artifactId> <version>2.1.5</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-json</artifactId> <version>1.17.1</version> </dependency> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-runtime-native</artifactId> <version>2.1.5</version> </dependency> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-compat-jbossweb</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-compat-tomcat</artifactId> <version>2.0.1</version> </dependency> </dependencies> <build> <finalName>notifications</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:j2ee="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_3.0.xsd"> <servlet> <servlet-name>AtmosphereServlet</servlet-name> <servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.clearcapital.notifications</param-value> </init-param> <init-param> <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>org.atmosphere.useWebSocketAndServlet3</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>AtmosphereServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
from ValidationsRouter.java:
@Path("/validations") public class ValidationsRouter { @Context private AtmosphereResource atmosphereResource; @GET public SuspendResponse<ValidationNotification> subscribe(@Context UriInfo uri) throws URISyntaxException, NamingException, IOException { return new ValidationsResource().subscribe(uri, atmosphereResource); } @Broadcast @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Broadcastable broadcast(ValidationNotification message, @Context UriInfo uri) { return new ValidationsResource().broadcast(message, uri); } }
from ValidationsResource.java:
public class ValidationsResource extends ResourceBase<ValidationNotification> {
from ResourceBase.java:
public abstract class ResourceBase<T> { public SuspendResponse<T> subscribe(UriInfo uri, AtmosphereResource atmosphereResource) throws URISyntaxException, NamingException, IOException { Broadcaster myBroadcaster = BroadcasterFactory.getDefault().lookup(uri.getPath(), true); return new SuspendResponse.SuspendResponseBuilder<T>().broadcaster(myBroadcaster).outputComments(true) .addListener(new EventsLogger()).type(MediaType.APPLICATION_JSON_TYPE).build(); } public Broadcastable broadcast(T message, UriInfo uri) { Broadcaster myBroadcaster = BroadcasterFactory.getDefault().lookup(uri.getPath(), true); return new Broadcastable(message, "", myBroadcaster); } }
============= End of original message =======================
Updated on October 15, 2014:
I returned to this problem after a while in another project. After updating the project with the new version of the Atmosphere (2.2.3), I see no difference in behavior. It still remains that when the service starts on a different computer than the client is running, atmosphere broadcasts are not received by the client until Tomcat disconnects. When you start the client and the service on the same computer, everything works as expected.
So, to determine where the problem lies, I started using only one of the Atmosphere app's applications in my testing. I am using the jersey-pubsub example application (with minor updates to compile it) because the one that is closest to what I'm trying to do. I created a completely new server environment to run a sample application to minimize any variables based on Linux configuration. Here are the new steps - maybe someone can reproduce the behavior.
To get a new server with Tomcat7 installed (I use Vagrant and VirtualBox for these tests):
host$ vagrant init ubuntu/trusty64 host$ nano Vagrantfile
change the network configuration to a known IP: config.vm.network :private_network, ip: "10.1.1.2"
host$ vagrant box add https://vagrantcloud.com/ubuntu/boxes/trusty64/versions/1/providers/virtualbox.box host$ vagrant up ------------------ vagrant$ sudo apt-get update vagrant$ sudo apt-get upgrade vagrant$ sudo apt-get install tomcat7
Following these steps, I ended up with Tomcat 7.0.52 and Ubuntu 14.04
I have a copy of a jersey-pubsub project (without a parent project, etc.) that I create with
host$ mvn clean install
and then expand
host$ cp target/jersey-pubsub.war ../../vagrants/trusty64/ vagrant$ sudo cp /vagrant/jersey-pubsub.war /var/lib/tomcat7/webapps
Then I can check this with telnet, for example:
host$ telnet 10.1.1.2 8080 <return> GET /jersey-pubsub/pubsub/foo HTTP/1.0 <return> <return>
At this point, telnet continues to wait for data. In the tomcat server log I see an entry:
20:33:55.805 [http-bio-8080-exec-1] INFO oasamples.pubsub.EventsLogger - onSuspend(): 10.1.1.1:59357
Then I send a POST request to the server. I use POSTman for this, but I do not think this is important.
POST http://10.1.1.2:8080/jersey-pubsub/pubsub/foo Accept: text/html Content-Type: application/x-www-form-urlencoded message: <html><body><p>This is a test!</p></body></html>
In the server log, I see that the server believes that it is sending a message:
20:34:06.990 [Atmosphere-Shared-AsyncOp-0] INFO oasamples.pubsub.EventsLogger - onBroadcast(): <html><body><p>This is a test!</p></body></html>
but nothing is displayed in the telnet client. I can repeat infinity and see no result until I turn off Tomcat, after which all broadcast messages will be immediately sent to Telnet.
In contrast, if I deploy the service on a tomcat instance running on the host machine, or if I run telnet inside the vagrant instance, everything works as expected:
vagrant$ telnet localhost 8080 <return> GET /jersey-pubsub/pubsub/foo HTTP/1.0 <return> <return>
If I then send the same POST request, I immediately receive a broadcast message in telnet. The behavior seems to be that if the service and the client are not on the same computer, then network traffic is held back while Tomcat is running.
Here is the source code for my copy of the jersey-pubsub sample, with enough changes to make it compile and build as a separate project.
EventsLogger.java: (No change, except to remove @Override in 2 places to make Eclipse stop complaining)
FileResource.java: (No change)
JerseyPubSub.java: (no change)
web.xml: (No change)
pom.xml (modified to create as standalone, without reference to the parent pom):
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.atmosphere.samples</groupId> <artifactId>atmosphere-jersey-pubsub</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>atmosphere-jersey-pubsub</name> <url>http://maven.apache.org</url> <properties> <atmosphere-version>2.2.3</atmosphere-version> <client-version>2.2.3</client-version> <logback-version>1.0.13</logback-version> </properties> <repositories> <repository> <id>oss.sonatype.org</id> <url>http://oss.sonatype.org/content/repositories/releases</url> </repository> <repository> <id>oss.sonatype.org-snapshot</id> <url>http://oss.sonatype.org/content/repositories/snapshots</url> </repository> <repository> <id>jboss</id> <url>https://repository.jboss.org/nexus/content/groups/public/</url> </repository> <repository> <id>codehaus</id> <name>repository.codehaus.org</name> <url>http://repository.codehaus.org</url> </repository> <repository> <id>codehaus-snapshots</id> <url>http://snapshots.repository.codehaus.org</url> </repository> <repository> <id>maven.java.net</id> <url>https://maven.java.net/content/groups/public/</url> </repository> </repositories> <dependencyManagement> <dependencies> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-runtime</artifactId> <version>${atmosphere-version}</version> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-servlet_3.0_spec</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback-version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.atmosphere.client</groupId> <artifactId>javascript</artifactId> <version>${client-version}</version> <type>war</type> </dependency> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-jersey</artifactId> <version>${atmosphere-version}</version> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-servlet_3.0_spec</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>${logback-version}</version> </dependency> </dependencies> <build> <finalName>jersey-pubsub</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>