Hot questions for Using GlassFish in nginx

Question:

I'm trying to make a chat app using Websockets in JavaEE using Glassfish server and Javascript AngularJS for client side. I am using Nginx to proxy the connections.

Nginx config:

location /ws/ {
        proxy_pass http://10.10.127.78:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

In Glassfish admin page, I enabled the web socket mode and I ensured that the port is set to 8080.

The server side path is set to : @ServerEndpoint(value = "/ws/chat/{room}").

OK, now the issue is that, I tried to connect to many addresses and each one gives some problems:

  1. Tried to connect as localhost: var serviceLocation = 'ws://localhost:8080/ws/chat/all/'; here the connection results in a WebSocket connection to 'ws://localhost:8080/ws/chat/all/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

  2. Tried to connect to server ip: var serviceLocation = 'ws://10.10.127.78:8080/ws/chat/'; it results in connection timeout WebSocket connection to 'ws://10.10.127.78:8080/ws/chat/all/' failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT

  3. Tried to connect to the domain: var serviceLocation = 'ws://test.domain.com:8080/ws/chat/'; it results in a 404 WebSocket connection to 'ws://test.domain.com:8080/ws/chat/all/' failed: Error during WebSocket handshake: Unexpected response code: 404

Can't get this to work. What can I try next ? Or what am I doing wrong ? If you need more info, leave a comment. Thank you, and hope someone can help.

I'm hosting all of this environments on Jelastic.

UPDATE: Javascript code

var wsocket;
var serviceLocation = 'ws://localhost:8080/ws/chat/';
//var serviceLocation = 'ws://10.10.127.78:8080/ws/chat/';
var nickName = $rootScope.loggedUser.user.firstName + " " + 
        $rootScope.loggedUser.user.lastName;
var room = 'all';
var $chatWindow = $('#response');


function onMessageReceived(evt) {
    var msg = JSON.parse(evt.data);
    var $appendedLine = $('<tr class="hidden-xs"><td>'+msg.received+' | '+msg.sender+'</td><td>'+msg.message+'</td></tr>\n\
                        <tr class="visible-xs"><td>'+msg.sender+': '+msg.message+'</td></tr>');
    $chatWindow.append($appendedLine);
}

function sendMessage() {
    var msg = '{"message":"' + $scope.message + '", "sender":"'
                    + nickName + '", "received":""}';
    wsocket.send(msg);
    $scope.message = "";
}

function connectToChatserver() {
    console.log(serviceLocation + room);
    wsocket = new WebSocket(serviceLocation + room);
    console.log("> ok");
    wsocket.onmessage = onMessageReceived;
    console.log("> connected to Web Socket server");
}


$scope.submitMessage = function() {
    sendMessage();
};


connectToChatserver();

UPDATE: local test

I tried to mirror the server to the local machine and test it locally without Nginx and it worked. Did I miss anything on Nginx, and it might be the cause of the issue ? Verified the proxy pass ip and port and they check out.


Answer:

Ok, I found out what was wrong and I want to share. The problem was the deployment to a glassfish 3.1.4 server with jdk 7 from a build made on a glassfish 4.1 with jdk 8 server. Ofc the issue occurred because of the jdk version, but because of the server version difference, the server didn't throw any version exception issues so I didn't see where the problem was coming from (forgot I was building from a jdk 8 server).

Question:

I have a bean dedicated to export a CSV and due to the big amount of data it takes around 15 minutes o more and I get a timeout.

In order to solve this I thought about adding some kind of configuration only for this bean, as I mustn't increase Glassfish timeouts. So in my local machine I changed Glassfish timeout configurations to make some tests and reduce those 15 minutes (Request Timeout from Network Listeners and IDLE timeout from Thread Pools) and, by adding just a @Transactional annotation it worked, but when I used a preproduction machine it didn't.

Both are Glassfish 4.1.1, but in preproduction I also use Nginx. But trying through the 8181 port (HTTPS, which my Nginx doesn't control) it didn't work neither.

So I've been trying different solutions:

  • @Transactional (cause transactions timeout is 0 in Glassfish)
  • Use a EJB
  • @StatefulTimeout
  • @TransactionManagement(TransactionManagementType.CONTAINER) and @TransactionAttribute(TransactionAttributeType.REQUIRED)

None of this worked in preproduction. Can someone guide me a bit?

Edit: This is my configuration of Nginx.

server {
    listen       80;
    client_max_body_size 900M;
    server_name *my config*;
    location / {
        proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_buffering off;
            proxy_ignore_client_abort off;
            proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:8080;
    }
}

The thing is even though setting timeouts in Glassfish and reducing it to 1 minute, in preproducction I can't avoid that timeout and the connection closes after that minute.


Answer:

Long transactions are in general not a good idea for an online application. That taken into consideration it makes probably sense to limit connection timeout to 1 minute.

What you can do is to perform the actual CSV export asynchronously in background. By annotating a business method of a stateless session bean with @Async, the method will be executed in a separate thread, while the original request is completed immediately. Transaction timeout of @Async annotated method need to be increased, of course, but that you might have already done. Since the initial request ends immediately, timeout isn't an issue anymore. And export is performed in background on the server, without a connection to the client.

Note: Business methods annotated with @Async are by default started in a new transaction.

The remaining problem is to properly report back the result of CSV export to the user, if that is needed. After the initial request, the user can only be told, that the CSV export job has been triggered, but not whether it finished successfully. One possible solution is, that the long-running export method returns a FutureResult<ExportJobResult>, that is registered in a @Singleton keyed by a job-id. The generated job-id (e.g. an UUID) is returned to the client with the initial response. The client can then poll, let's say every 10 seconds, for status of the Future object (checking for isDone()). A more elaborated solution would be a complete job management framework, that persists job status.

Question:

I am running a glassfish webserver behind a nginx proxy (which resolves domains). I want to store @SessionScoped data on the Webserver. My problem is, that nginx opens a new session everytime it connects to glassfish and that session is in no way connected to the client. The client itself has an active session on nginx.

Glassfish seems to be aware of the fact, that something is wrong with the way the sessions are being handled. It manipulates links within the application to keep the session intact: ".../ProxyTest/page2.jsf;jsessionid=4372da9b75a2043875c73b12f998" It does not do that if I connect directly to the webserver(without the proxy) This behaviour is totally unacceptable to me, I need that manipulation gone, but still have the session.

I would like to have an individual and persistent session on glassfish for every user that connects to it via the proxy without having to pass the sessionID in the url.

nginx does set the cookie, but the browser does not send it back. I'm assuming the cookie is formatted correctly:

Set-Cookie:"JSESSIONID=84c5e4e43538da045cc1d8f2a629; Path=/ProxyTest; HttpOnly"

my nginx settings: server { listen 80; server_name www.abc.ch;

location / {
    proxy_set_header customer abc;
    access_log off;
    proxy_pass http://127.0.0.1:8081/ProxyTest/;
    proxy_set_header Host $host;
    proxy_redirect     off;
    proxy_set_header X-Real-IP $remote_addr;
}

}

server {
    listen       80;
    server_name  www.onlineshop.ch;

location / {
    proxy_set_header customer onlineshop;
    access_log off;
    proxy_pass http://127.0.0.1:8081/ProxyTest/;
    proxy_set_header Host $host;
    proxy_redirect     off;
    proxy_set_header X-Real-IP $remote_addr;

}

The problem is now, that the browser does not send back the cookie, or for some reason it doesn't get throught to the server. On every request the server generates a new session ID. Confirming that is that data stored in a @SessionScoped bean gets lost.


Answer:

It helps if you post your NGINX proxy config. A basic reverse proxy config looks like this:

...
http {
    ....
    server {
        listen       80;
        server_name  www.yourdomain.com;

        location / {
            access_log off;
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Host $host;
            proxy_redirect     off;
            proxy_set_header X-Real-IP $remote_addr;
            ...
        }
        ....
    }
}