├── .gitignore ├── README.md ├── responsive-parent.iml ├── scripts ├── inverted.gnuplot └── process.sh ├── spring-websocket-performance-client ├── main_ws.js ├── main_xhr.js ├── package.json └── patch.diff └── spring-websocket-performance-endpoint ├── deployTomcat8.sh ├── pom.xml ├── shutdownTomcat8.sh ├── spring-websocket-performance-endpoint.iml ├── src └── main │ ├── filtered │ └── cors.properties │ ├── java │ └── org │ │ └── cloudmark │ │ └── samples │ │ ├── performance │ │ ├── dto │ │ │ ├── Ping.java │ │ │ └── User.java │ │ └── services │ │ │ ├── AuthenticationController.java │ │ │ └── PingController.java │ │ └── security │ │ ├── realm │ │ └── SimpleAuthorizingRealm.java │ │ └── token │ │ └── SimpleAuthToken.java │ ├── resources │ ├── log4j.xml │ └── ping.properties │ └── webapp │ └── WEB-INF │ ├── applicationContext.xml │ ├── shiro-secuirty.xml │ ├── web-endpoint-servlet.xml │ └── web.xml └── web-endpoint.iml /.gitignore: -------------------------------------------------------------------------------- 1 | .name 2 | .idea/** 3 | results/** 4 | spring-websocket-performance-endpoint/.idea/** 5 | spring-websocket-performance-endpoint/target/** 6 | spring-websocket-performance-client/node_modules/** 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | An experiment to the spring websockets infrastucture in order to answer some key questions and help tweak the various parameters found in the spring web socket infrastructure e.g. client-inbound-channel executor pools. 4 | 5 | The project is made up of two parts: 6 | 7 | * spring-websocket-performance-client 8 | * spring-websocket-performance-endpoint 9 | 10 | spring-websocket-performance-client consist of two seperate node application which connect to the server componet using different transports; xhr_streaming client (main_xhr.js), web socket client (main_ws.js). 11 | 12 | spring-websocket-performance-endpoint is the server side component which provides an authentication flow, a sockjs endpoint, and a periodic message flow for our analysis. 13 | 14 | The two client variants run on `node` while the server side component runs on `Tomcat 8.0.3+`, Jetty 9.0.7+` or `Glassfish 4.0`. 15 | 16 | # Tomcat 8 17 | The application has been test `Tomcat 8.0.3`. For Tomcat 8, set `TOMCAT8_HOME` as an environment variable and use [deployTomcat8.sh](https://github.com/cloudmark/spring-websockets-performance-testing/blob/master/spring-websocket-performance-endpoint/deployTomcat8.sh) and [shutdownTomcat8.sh](https://github.com/cloudmark/spring-websockets-performance-testing/blob/master/spring-websocket-performance-endpoint/shutdownTomcat8.sh) in the `spring-websocket-performance-client` directory. 18 | 19 | Open a browser an go to in order to make sure that the application has been deployed successfully. 20 | 21 | # Jetty 9 22 | The easiest way to run on Jetty 9 is mvn jetty:run. 23 | 24 | Open a browser an go to in order to make sure that the application has been deployed successfully. 25 | 26 | # Node 27 | The client side components have been tested on `Node v0.10.22`. In order to run the client first make sure that the server endpoint is up and running and then execute 28 | 29 | `node main_xhr.js ` 30 | 31 | or 32 | 33 | `node main_ws.js ` 34 | 35 | depending on the transport you are analysing. 36 | 37 | **Note** The client component is intended to be executed on a number of processes, each process will output some data (explained later on) and we will use the `` to identify a particular process. The name you give as an identifier is not important, my personal perference is p_0, p_1 etc. 38 | 39 | To run multiple process on a single machine use the following script: 40 | 41 | ``` 42 | for p in `seq 0 1 399` 43 | do 44 | PROCESS="p_"$p 45 | echo "Process [$PROCESS]"; 46 | node main_ws.js "$PROCESS" 2>&1 > ./logs_ws/$PROCESS &; sleep 0.25; 47 | done 48 | ``` 49 | 50 | # A Little More Detail 51 | As outlined earlier the server provides three main services: 52 | 53 | * Authentication Flow 54 | * Stomp Endpoint (SockJs Stomp Endpoint) 55 | * Message Flow 56 | 57 | ## Authentication Flow. 58 | Once the endpoint is running, you can use 59 | 60 | 61 | 62 | to authentication together with the `username` and `password` header. The implemented authentication realm is pretty stupid - [SimpleAuthorizingRealm](https://github.com/cloudmark/spring-websockets-performance-testing/blob/master/spring-websocket-performance-endpoint/src/main/java/org/cloudmark/samples/security/realm/SimpleAuthorizingRealm.java) - and only requires that the `username` and `password` are the same. 63 | 64 | *Note* Authentication enables the web-socket session to identify the principle. This correlation may be used to gather some statistics from the server side. 65 | 66 | ## Message Flow 67 | There are three topics which provide a stream of data to the client. 68 | 69 | * /topic/ping.1 70 | * /topic/ping.2 71 | * /topic/ping.3 72 | 73 | Each of these topics write the following json 74 | ``` 75 | { 76 | time: , 77 | data: 78 | } 79 | ``` 80 | on the response stream periodically every 1, 2, and 3 seconds respectively. **Note** the length of the random sequence of characters is controlled from the `dummyBytes` property in the [ping.properties](https://github.com/cloudmark/spring-websockets-performance-testing/blob/master/spring-websocket-performance-endpoint/src/main/resources/ping.properties) property file. 81 | 82 | 83 | # WS Client 84 | The web socket client uses a modified version of the `sockjs-client-ws` client library. Executing 85 | 86 | `node main_ws.js p_0` 87 | 88 | would perform the following sequence of actions: 89 | 90 | 1. Authenticate with the backend code using the username p_0 and password p_0 91 | 2. Register to /topic/ping.1 (sub-0) 92 | 3. Register to /topic/ping.2 (sub-1) 93 | 4. Register to /topic/ping.3 (sub-2) 94 | 5. Output the username, subscription id, server time, time difference from the last packet received from this subscription. 95 | 96 | 97 | An example output for sub-0 is the following 98 | p_0 SUB-0 1395622591670 0 99 | p_0 SUB-0 1395622592670 1001 100 | p_0 SUB-0 1395622593671 1002 101 | p_0 SUB-0 1395622594673 1005 102 | p_0 SUB-0 1395622595674 1000 103 | ... 104 | 105 | 106 | # XHR Client 107 | 108 | The xhr streaming client uses a modified version of the `sockjs-client` client library. Executing 109 | 110 | `node main_xhr.js p_0` 111 | 112 | results in the same sequence of actions outlined in the websocket variant. *Note* that this would use the XHR Streaming transport instead. 113 | 114 | # Statistics 115 | 116 | Although the data gather by each client (remember there will be several of these runing at one point) is simple, using the different client log files we can work out the average message arrival of the clients and the standard deviation from that mean. This will be our guide when optimising the various web-socket message-broker parameters. 117 | 118 | Using the above script each process will output the data to a file whose name matches it's process name. Since we have registered with three subscriptions we will need to seperate the collected statistics in their own individual subscription file. To do this we will use the following script. 119 | ``` 120 | for file in `ls -1 p*` 121 | do 122 | cat $file | grep SUB-0 > ./sub-0/$file 123 | cat $file | grep SUB-1 > ./sub-1/$file 124 | cat $file | grep SUB-2 > ./sub-2/$file 125 | done 126 | 127 | ``` 128 | 129 | Now that we have the process data per subscription we will group all this data based on the server timestamp, something like an inverted index. So we will need to transform the process files e.g. 130 | 131 | ``` 132 | p_0 SUB-0 1395622591670 0 133 | p_0 SUB-0 1395622592670 1001 134 | p_0 SUB-0 1395622593671 1002 135 | p_0 SUB-0 1395622594673 1005 136 | p_0 SUB-0 1395622595674 1000 137 | ``` 138 | 139 | ``` 140 | p_1 SUB-0 1395622591670 0 141 | p_1 SUB-0 1395622592670 1001 142 | p_1 SUB-0 1395622593671 1002 143 | p_1 SUB-0 1395622594673 1005 144 | p_1 SUB-0 1395622595674 1000 145 | ``` 146 | 147 | to a single file containing the following data. 148 | 149 | ``` 150 | 151 | ``` 152 | 153 | an example of this is given below. We will than plot this file to make our observations. 154 | 155 | ``` 156 | 1395622593.671 5 1000 1002 1000.8 0.83666 157 | 1395622594.673 9 1003 1005 1004.56 0.726498 158 | 1395622595.674 12 999 1002 1000.42 0.792969 159 | 1395622596.676 16 1006 1010 1007.81 0.981074 160 | 1395622597.677 20 992 998 994.9 1.37267 161 | 1395622598.677 24 996 1002 998.667 1.57885 162 | ``` 163 | 164 | To perform this transformation you can use the process.sh file in the scripts folder. This sruot will also plot a graph of the results using gnuplot and the graph file `inverted.gnuplot` in the scripts folder. 165 | 166 | -------------------------------------------------------------------------------- /responsive-parent.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /scripts/inverted.gnuplot: -------------------------------------------------------------------------------- 1 | # raw using 1:4 with points lc 9 title '', 2 | set datafile separator " " 3 | set terminal png size 4800, 1200 4 | set xlabel "Time" 5 | set xdata time 6 | set timefmt "%s" 7 | set format x "%M:%S" 8 | set key left top 9 | set xtics 10 rotate by 90 right 10 | set mxtics 1 11 | set grid 12 | set yrange [2800:3200] 13 | 14 | set multiplot layout 3, 1; 15 | set title "Latency Log [".name."]" 16 | set ylabel "Latency Log" 17 | set logscale y 18 | set style fill transparent solid 0.5 noborder 19 | plot filename using 1:($5-$6):($5+$6) with filledcurves lc rgb '#ffcccc' title 'StdDev',\ 20 | filename using 1:5 with lines lc rgb 'red' title 'Mean',\ 21 | filename using 1:5 with points lc rgb 'red' title '',\ 22 | filename using 1:3 with lines lw 2 lt 3 lc 1 title 'Min', \ 23 | filename using 1:4 with lines lw 2 lt 3 lc 2 title 'Max' 24 | 25 | set title "Latency [".name."]" 26 | set ylabel "Latency " 27 | set nologscale y 28 | set style fill transparent solid 0.5 noborder 29 | plot filename using 1:($5-$6):($5+$6) with filledcurves lc rgb '#ffcccc' title 'StdDev',\ 30 | filename using 1:5 with lines lc rgb 'red' title 'Mean',\ 31 | filename using 1:5 with points lc rgb 'red' title '',\ 32 | filename using 1:3 with lines lw 2 lt 3 lc 1 title 'Min', \ 33 | filename using 1:4 with lines lw 2 lt 3 lc 2 title 'Max' 34 | 35 | set title "Connections [".name."]" 36 | set ylabel "Count" 37 | plot filename using 1:2 with lines lw 2 lt 3 title 'Count', \ 38 | filename using 1:2 t '' with points 39 | 40 | #set title "Threshold [".name."]" 41 | #set ylabel "Thresholded" 42 | #plot filename using 1:7 with lines lw 2 lt 3 title 'Threshold', \ 43 | # filename using 1:7 t '' with points 44 | 45 | 46 | -------------------------------------------------------------------------------- /scripts/process.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | DIR=$1 4 | OUT=$2 5 | THRESHOLD=0 6 | mkdir $OUT 7 | echo "Processing Directory - $DIR" 8 | rm -f "$OUT/$DIR-process" 9 | rm -rf "$OUT/$DIR-process-1" 10 | rm -rf "./$OUT/$DIR-inverted-graph-std" 11 | rm -rf "./$OUT/$DIR-thresholds-max" 12 | rm -rf "./$OUT/$DIR-thresholds-min" 13 | 14 | for file in `ls -1 $DIR` 15 | do 16 | echo "Processing File $file" 17 | cat $DIR/$file | awk '($4 != 0) {print int($3 / 1000) "." ($3%1000), $1, $2, $4}' >> "$OUT/$DIR-process-1" 18 | done 19 | cat "$OUT/$DIR-process-1" | sort >> "$OUT/$DIR-process" 20 | 21 | for sample in `cat $OUT/$DIR-process-1 | awk '{print $1}' | sort | uniq` 22 | do 23 | echo "Processing -> $sample" 24 | AVERAGE=`cat "$OUT/$DIR-process-1" | awk -v sample=$sample -v threshold=$THRESHOLD '($1 == sample && $4 >= threshold) {sum += $4; count +=1} END {print sum/count}'` 25 | ROW=$(cat "$OUT/$DIR-process-1" |\ 26 | awk -v avg=$AVERAGE -v sample=$sample -v threshold=$THRESHOLD \ 27 | 'BEGIN { 28 | min = avg; 29 | max = avg; 30 | } 31 | ($1 == sample && $4 >= threshold ) { 32 | if ($4 < min) min = $4; 33 | if ($4 > max) max = $4; 34 | sum += $4; 35 | std += (($4 - avg) * ($4 - avg)); 36 | count +=1 37 | } 38 | ($1 == sample && $4 < threshold) { 39 | threshold_count += 1; 40 | } 41 | END {print sample, count, min, max, avg, sqrt(std/(count-1)), threshold_count}') 42 | 43 | MIN=`echo $ROW | awk '{print $3}'` 44 | echo "Finding Samples Which [Min: $MIN]" 45 | MIN_PS=`cat "$OUT/$DIR-process-1" | awk -v sample=$sample -v min=$MIN '($1 == sample && $4 == min) {print $2}'` 46 | for min_p in $MIN_PS 47 | do 48 | echo "Sample $min_p contains min [$MIN] @ [$sample]" >> "./$OUT/$DIR-thresholds-min" 49 | cat $OUT/$DIR-process-1 | grep "$min_p\ " | grep "$sample" --context=5 >> "./$OUT/$DIR-thresholds-min" 50 | echo "" >> "./$OUT/$DIR-thresholds-min" 51 | echo "" >> "./$OUT/$DIR-thresholds-min" 52 | done 53 | 54 | MAX=`echo $ROW | awk '{print $4}'` 55 | echo "Finding Samples Which [Max: $MAX]" 56 | MAX_PS=`cat "$OUT/$DIR-process-1" | awk -v sample=$sample -v max=$MAX '($1 == sample && $4 == max) {print $2}'` 57 | for max_p in $MAX_PS 58 | do 59 | echo "Sample $max_p contains max [$MAX] @ [$sample]" >> "./$OUT/$DIR-thresholds-max" 60 | cat $OUT/$DIR-process-1 | grep "$max_p\ " | grep "$sample" --context=5 >> "./$OUT/$DIR-thresholds-max" 61 | echo "" >> "./$OUT/$DIR-thresholds-max" 62 | echo "" >> "./$OUT/$DIR-thresholds-max" 63 | done 64 | 65 | echo $ROW >> "./$OUT/$DIR-inverted-graph-std" 66 | done 67 | 68 | #echo "Generating Plot" 69 | #rm -rf $OUT/$DIR-graph.png 70 | gnuplot -e "name='$DIR';filename='./$OUT/$DIR-inverted-graph-std';raw='./$OUT/$DIR-process'" inverted.gnuplot > $OUT/$DIR-graph.png 71 | 72 | -------------------------------------------------------------------------------- /spring-websocket-performance-client/main_ws.js: -------------------------------------------------------------------------------- 1 | var sjsc = require('sockjs-client-ws'); 2 | var stomp = require('stomp'); 3 | var timers = require('timers'); 4 | var http = require('http'); 5 | 6 | var time = {}; 7 | 8 | if (process.argv.length != 3) { 9 | throw new Error("Expected Processor Identifier"); 10 | } 11 | 12 | var username = process.argv[2]; 13 | // console.log("Running under username: ", username); 14 | 15 | // An object of options to indicate where to post to 16 | var post_options = { 17 | host: 'localhost', 18 | port: '8080', 19 | path: '/spring-websocket-performance-endpoint/login', 20 | method: 'GET', 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | 'Content-Length': 0, 24 | 'username': username, 25 | 'password': username 26 | } 27 | }; 28 | // Set up the request 29 | var post_req = http.request(post_options, function(res) { 30 | res.setEncoding('utf8'); 31 | res.on('data', function (chunk) { 32 | var session = JSON.parse(chunk).session; 33 | // Now that we have received a reply we will connect. 34 | var client = sjsc.create("http://localhost:8080/spring-websocket-performance-endpoint/comet", { 35 | debug: false, 36 | devel: false 37 | }); 38 | client.on('connection', function () { 39 | var sclient = new stomp.Stomp({socket: client, log: function(){}}); 40 | sclient.connect(); 41 | sclient.on('connected', function() { 42 | sclient.subscribe({ 43 | 'destination': '/topic/ping.1', 44 | 'id':'sub-0' 45 | }, function(msg){ 46 | var current = new Date().getTime(); 47 | if (time['sub-0'] !== undefined) old = time['sub-0'] 48 | else old = current; 49 | console.log(username, "SUB-0", JSON.parse(msg).time, current - old); 50 | time['sub-0'] = current; 51 | }); 52 | sclient.subscribe({ 53 | 'destination': '/topic/ping.2', 54 | 'id':'sub-1' 55 | }, function(msg){ 56 | var current = new Date().getTime(); 57 | if (time['sub-1'] !== undefined) old = time['sub-1'] 58 | else old = current; 59 | console.log(username, "SUB-1", JSON.parse(msg).time, current - old); 60 | time['sub-1'] = current; 61 | }); 62 | 63 | sclient.subscribe({ 64 | 'destination': '/topic/ping.3', 65 | 'id':'sub-2' 66 | }, function(msg){ 67 | var current = new Date().getTime(); 68 | if (time['sub-2'] !== undefined) old = time['sub-2'] 69 | else old = current; 70 | console.log(username, "SUB-2", JSON.parse(msg).time, current - old); 71 | time['sub-2'] = current; 72 | }); 73 | 74 | }); 75 | 76 | }); 77 | client.on('data', function (msg) { 78 | //console.log("MSG", msg); 79 | }); 80 | client.on('error', function (e) { 81 | // console.log("ERROR",e); 82 | }); 83 | }); 84 | }); 85 | 86 | post_req.write(''); 87 | post_req.end(); 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /spring-websocket-performance-client/main_xhr.js: -------------------------------------------------------------------------------- 1 | var sjsc = require('sockjs-client'); 2 | var stomp = require('stomp'); 3 | var timers = require('timers'); 4 | var http = require('http'); 5 | 6 | var time = {}; 7 | 8 | if (process.argv.length != 3) { 9 | throw new Error("Expected Processor Identifier"); 10 | } 11 | 12 | var username = process.argv[2]; 13 | // console.log("Running under username: ", username); 14 | 15 | // An object of options to indicate where to post to 16 | var post_options = { 17 | host: 'localhost', 18 | port: '8080', 19 | path: '/spring-websocket-performance-endpoint/login', 20 | method: 'GET', 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | 'Content-Length': 0, 24 | 'username': username, 25 | 'password': username 26 | } 27 | }; 28 | // Set up the request 29 | var post_req = http.request(post_options, function(res) { 30 | res.setEncoding('utf8'); 31 | res.on('data', function (chunk) { 32 | var session = JSON.parse(chunk).session; 33 | var client = sjsc.create("http://localhost:8080/spring-websocket-performance-endpoint/comet", { 34 | debug: false, 35 | devel: false, 36 | protocols_whitelist: ['xhr-streaming'] 37 | }); 38 | client.on('connection', function () { 39 | var sclient = new stomp.Stomp({socket: client, log: function(){},}); 40 | sclient.connect(); 41 | sclient.on('connected', function() { 42 | client.set_cookies(session); 43 | 44 | timers.setInterval(function(){ 45 | client.write('\n'); 46 | }, 10000); 47 | sclient.subscribe({ 48 | 'destination': '/topic/ping.1', 49 | 'id':'sub-0' 50 | }, function(msg){ 51 | var current = new Date().getTime(); 52 | if (time['sub-0'] !== undefined) old = time['sub-0'] 53 | else old = current; 54 | console.log(username, "SUB-0", JSON.parse(msg).time, current - old); 55 | time['sub-0'] = current; 56 | }); 57 | sclient.subscribe({ 58 | 'destination': '/topic/ping.2', 59 | 'id':'sub-1' 60 | }, function(msg){ 61 | var current = new Date().getTime(); 62 | if (time['sub-1'] !== undefined) old = time['sub-1'] 63 | else old = current; 64 | console.log(username, "SUB-1", JSON.parse(msg).time, current - old); 65 | time['sub-1'] = current; 66 | }); 67 | 68 | sclient.subscribe({ 69 | 'destination': '/topic/ping.3', 70 | 'id':'sub-2' 71 | }, function(msg){ 72 | var current = new Date().getTime(); 73 | if (time['sub-2'] !== undefined) old = time['sub-2'] 74 | else old = current; 75 | console.log(username, "SUB-2", JSON.parse(msg).time, current - old); 76 | time['sub-2'] = current; 77 | }); 78 | 79 | }); 80 | 81 | }); 82 | 83 | client.on('error', function (e) { 84 | console.log("ERROR",e); 85 | }); 86 | }); 87 | }); 88 | 89 | post_req.write(''); 90 | post_req.end(); 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /spring-websocket-performance-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-websocket-performance-client", 3 | "author": "Mark Galea", 4 | "dependencies": { 5 | "sockjs-client": "~0.1.3", 6 | "sockjs-client-ws": "~0.1.0", 7 | "stomp": "~0.1.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /spring-websocket-performance-client/patch.diff: -------------------------------------------------------------------------------- 1 | diff --git a/spring-websocket-performance-client/node_modules/sockjs-client-ws/lib/sockjs-client.js b/spring-websocket-performance-client/node_modules/sockjs-client-ws/lib/sockjs-client.js 2 | index c505371..a7b6be7 100644 3 | --- a/spring-websocket-performance-client/node_modules/sockjs-client-ws/lib/sockjs-client.js 4 | +++ b/spring-websocket-performance-client/node_modules/sockjs-client-ws/lib/sockjs-client.js 5 | @@ -98,8 +98,9 @@ 6 | if (this.isReady || this.isClosing || this.isClosed) { 7 | return; 8 | } 9 | - var that = this 10 | - var transport = new WebSocketTransport('ws://'+this.server.host+this.server.path+'/websocket'); 11 | + var that = this; 12 | + var url = 'ws://'+this.server.host+this.server.path+'/websocket'; 13 | + var transport = new WebSocketTransport(url); 14 | transport.write = function(writeBuffer){ 15 | for(var i in writeBuffer){ 16 | var message = writeBuffer[i]; 17 | diff --git a/spring-websocket-performance-client/node_modules/sockjs-client-ws/node_modules/ws/lib/Sender.js b/spring-websocket-performance-client/node_modules/sockjs-client-ws/node_modules/ws/lib/Sender.js 18 | index fc3b437..4a1d556 100644 19 | --- a/spring-websocket-performance-client/node_modules/sockjs-client-ws/node_modules/ws/lib/Sender.js 20 | +++ b/spring-websocket-performance-client/node_modules/sockjs-client-ws/node_modules/ws/lib/Sender.js 21 | @@ -106,7 +106,7 @@ Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, 22 | if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) { 23 | data = getArrayBuffer(data); 24 | } else { 25 | - data = new Buffer(data); 26 | + data = new Buffer('[' + data + ']'); 27 | } 28 | } 29 | 30 | diff --git a/spring-websocket-performance-client/node_modules/sockjs-client/lib/sockjs-client.js b/spring-websocket-performance-client/node_modules/sockjs-client/lib/sockjs-client.js 31 | index f439153..7f533dd 100644 32 | --- a/spring-websocket-performance-client/node_modules/sockjs-client/lib/sockjs-client.js 33 | +++ b/spring-websocket-performance-client/node_modules/sockjs-client/lib/sockjs-client.js 34 | @@ -53,7 +53,7 @@ 35 | }()); 36 | 37 | function SockJSClient (server) { 38 | - var parsed, serverId, sessionId; 39 | + var parsed, serverId, sessionId, cookies; 40 | 41 | parsed = url.parse(server); 42 | 43 | @@ -92,7 +92,12 @@ 44 | isReady: false, 45 | isClosing: false, 46 | isClosed: false, 47 | - 48 | + set_cookies: function(cookies){ 49 | + this.cookies = cookies; 50 | + }, 51 | + get_cookies: function(){ 52 | + return this.cookies; 53 | + }, 54 | connect: function () { 55 | if (this.isReady || this.isClosing || this.isClosed) { 56 | return; 57 | @@ -180,10 +185,9 @@ 58 | }, 59 | 60 | initialPayloadLength: 2049, 61 | - 62 | start: function (sm) { 63 | var request = {method: 'POST', 64 | - headers: {'Content-Length': 0}}, 65 | + headers: {'Content-Length': 0, 'Connection':'keep-alive'}}, 66 | clientRequest; 67 | util.shallowCopy(this.sjs.server, request); 68 | request.path += '/xhr_streaming'; 69 | @@ -192,12 +196,15 @@ 70 | clientRequest.end(); 71 | }, 72 | 73 | - write: function (message) { 74 | - var data = JSON.stringify(message), 75 | + write: function (message, cookies) { 76 | + var cookie= this.sjs.get_cookies(), 77 | + data = JSON.stringify(message), 78 | request = {method: 'POST', 79 | headers: { 80 | 'Content-Type': 'application/json', 81 | - 'Content-Length': Buffer.byteLength(data,'utf8')}}, 82 | + 'Content-Length': Buffer.byteLength(data,'utf8'), 83 | + 'Cookie': 'JSESSIONID = ' + cookie 84 | + }}, 85 | clientRequest; 86 | util.shallowCopy(this.sjs.server, request); 87 | request.path += '/xhr_send'; 88 | diff --git a/spring-websocket-performance-client/node_modules/stomp/lib/stomp.js b/spring-websocket-performance-client/node_modules/stomp/lib/stomp.js 89 | index bb51546..13b4ca1 100644 90 | --- a/spring-websocket-performance-client/node_modules/stomp/lib/stomp.js 91 | +++ b/spring-websocket-performance-client/node_modules/stomp/lib/stomp.js 92 | @@ -94,44 +94,16 @@ function parse_frame(chunk) { 93 | 94 | function _connect(stomp) { 95 | log = stomp.log; 96 | - 97 | - if (stomp.ssl) { 98 | - log.debug('Connecting to ' + stomp.host + ':' + stomp.port + ' using SSL'); 99 | - stomp.socket = tls.connect(stomp.port, stomp.host, stomp.ssl_options, function() { 100 | - log.debug('SSL connection complete'); 101 | - if (!stomp.socket.authorized) { 102 | - log.error('SSL is not authorized: '+stomp.socket.authorizationError); 103 | - if (stomp.ssl_validate) { 104 | - _disconnect(stomp); 105 | - return; 106 | - } 107 | - } 108 | - _setupListeners(stomp); 109 | - }); 110 | - } 111 | - else { 112 | - log.debug('Connecting to ' + stomp.host + ':' + stomp.port); 113 | - stomp.socket = new net.Socket(); 114 | - stomp.socket.connect(stomp.port, stomp.host); 115 | - _setupListeners(stomp); 116 | - } 117 | + _setupListeners(stomp); 118 | } 119 | 120 | function _setupListeners(stomp) { 121 | function _connected() { 122 | log.debug('Connected to socket'); 123 | - var headers = {}; 124 | - if (utils.really_defined(stomp.login) && 125 | - utils.really_defined(stomp.passcode)) { 126 | - headers.login = stomp.login; 127 | - headers.passcode = stomp.passcode; 128 | - } 129 | - if (utils.really_defined(stomp["client-id"])) { 130 | - headers["client-id"] = stomp["client-id"]; 131 | - } 132 | - if (utils.really_defined(stomp["vhost"])) { 133 | - headers["host"] = stomp["vhost"]; 134 | - } 135 | + var headers = { 136 | + "accept-version" : "1.1,1.0", 137 | + "heart-beat" : "10000,10000" 138 | + }; 139 | stomp_connect(stomp, headers); 140 | } 141 | 142 | @@ -178,12 +150,8 @@ function _setupListeners(stomp) { 143 | } 144 | stomp.emit("disconnected", error); 145 | }); 146 | - 147 | - if (stomp.ssl) { 148 | - _connected(); 149 | - } else { 150 | - socket.on('connect', _connected); 151 | - } 152 | + _connected(); 153 | + 154 | }; 155 | 156 | function stomp_connect(stomp, headers) { 157 | @@ -233,7 +201,7 @@ function send_command(stomp, command, headers, body, want_receipt) { 158 | function send_frame(stomp, _frame) { 159 | var socket = stomp.socket; 160 | var frame_str = _frame.as_string(); 161 | - 162 | + frame_str = frame_str; 163 | if (socket.write(frame_str) === false) { 164 | log.debug('Write buffered'); 165 | } 166 | @@ -248,13 +216,20 @@ function send_frame(stomp, _frame) { 167 | // 168 | function Stomp(args) { 169 | events.EventEmitter.call(this); 170 | + this.socket = args['socket']; 171 | + this.log = { 172 | + info: args['log'], 173 | + debug: args['log'], 174 | + warn: args['log'], 175 | + error: args['log'] 176 | + }; 177 | + 178 | 179 | this.port = args['port'] || 61613; 180 | this.host = args['host'] || '127.0.0.1'; 181 | this.debug = args['debug']; 182 | this.login = args['login'] || null; 183 | this.passcode = args['passcode'] || null; 184 | - this.log = new stomp_utils.StompLogging(this.debug); 185 | this._subscribed_to = {}; 186 | this.session = null; 187 | this.ssl = args['ssl'] ? true : false; 188 | @@ -325,6 +300,7 @@ Stomp.prototype.handle_new_frame = function(this_frame) { 189 | this.emit('receipt', this_frame.headers['receipt-id']); 190 | break; 191 | case "ERROR": 192 | + log.error(this_frame); 193 | this.emit('error', this_frame); 194 | break; 195 | default: 196 | @@ -354,20 +330,6 @@ Stomp.prototype.subscribe = function(headers, callback) { 197 | var destination = headers['destination']; 198 | headers['session'] = this.session; 199 | send_command(this, 'SUBSCRIBE', headers); 200 | - 201 | - /** 202 | - / Maybe we could subscribe to mulitple queues? 203 | - / if (destination instanceof Array) { 204 | - / for (var = i; i < 0; i++) { 205 | - / this._subscribed_to[destination[i]] = { enabled: true, callback: callback }; 206 | - / } 207 | - / } 208 | - / else { 209 | - / this._subscribed_to[destination] = { enabled: true, callback: callback }; 210 | - / } 211 | - / 212 | - */ 213 | - 214 | this._subscribed_to[destination] = { enabled: true, callback: callback }; 215 | this.log.debug('subscribed to: ' + destination + ' with headers ' + sys.inspect(headers)); 216 | }; 217 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/deployTomcat8.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$TOMCAT8_HOME" ]; then 2 | echo -e "\n\nPlease set TOMCAT8_HOME\n\n" 3 | exit 1 4 | fi 5 | 6 | mvn -DskipTests clean package 7 | 8 | rm -rf $TOMCAT8_HOME/webapps/spring-websocket-performance-endpoint* 9 | 10 | cp target/spring-websocket-performance-endpoint.war $TOMCAT8_HOME/webapps/ 11 | 12 | $TOMCAT8_HOME/bin/startup.sh -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | org.cloudmark.samples 6 | spring-websocket-performance-endpoint 7 | war 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | 4.0.3.RELEASE 12 | 13 | 14 | 15 | 16 | org.projectlombok 17 | lombok 18 | 1.12.2 19 | 20 | 21 | 22 | commons-codec 23 | commons-codec 24 | 1.7 25 | 26 | 27 | 28 | commons-collections 29 | commons-collections 30 | 3.2.1 31 | 32 | 33 | 34 | commons-lang 35 | commons-lang 36 | 2.6 37 | 38 | 39 | 40 | 41 | org.springframework 42 | spring-context 43 | ${org.springframework-version} 44 | 45 | 46 | commons-logging 47 | commons-logging 48 | 49 | 50 | 51 | 52 | 53 | org.springframework 54 | spring-messaging 55 | ${org.springframework-version} 56 | 57 | 58 | 59 | org.springframework 60 | spring-web 61 | ${org.springframework-version} 62 | 63 | 64 | 65 | org.springframework 66 | spring-webmvc 67 | ${org.springframework-version} 68 | 69 | 70 | 71 | org.springframework 72 | spring-websocket 73 | ${org.springframework-version} 74 | 75 | 76 | 77 | 78 | javax.servlet 79 | javax.servlet-api 80 | 3.1.0 81 | provided 82 | 83 | 84 | 85 | 86 | 87 | com.fasterxml.jackson.core 88 | jackson-databind 89 | 2.2.2 90 | 91 | 92 | 93 | 94 | org.projectreactor 95 | reactor-tcp 96 | 1.0.1.RELEASE 97 | 98 | 99 | 100 | org.apache.shiro 101 | shiro-core 102 | 1.2.2 103 | 104 | 105 | 106 | org.apache.shiro 107 | shiro-web 108 | 1.2.2 109 | 110 | 111 | 112 | org.apache.shiro 113 | shiro-spring 114 | 1.2.2 115 | 116 | 117 | 118 | org.apache.tomcat.embed 119 | tomcat-embed-core 120 | 8.0.5 121 | test 122 | 123 | 124 | org.apache.tomcat.embed 125 | tomcat-embed-websocket 126 | 8.0.5 127 | test 128 | 129 | 130 | org.apache.tomcat.embed 131 | tomcat-embed-logging-log4j 132 | 8.0.5 133 | test 134 | 135 | 136 | 137 | org.eclipse.jetty.websocket 138 | websocket-client 139 | 9.1.3.v20140225 140 | 141 | 142 | org.eclipse.jetty.websocket 143 | websocket-server 144 | 9.1.3.v20140225 145 | 146 | 147 | org.eclipse.jetty 148 | jetty-webapp 149 | 9.1.3.v20140225 150 | 151 | 152 | 153 | 154 | com.jayway.jsonpath 155 | json-path 156 | 0.9.1 157 | test 158 | 159 | 160 | 161 | org.slf4j 162 | slf4j-api 163 | 1.7.5 164 | 165 | 166 | 167 | org.slf4j 168 | jcl-over-slf4j 169 | 1.7.5 170 | 171 | 172 | 173 | org.slf4j 174 | slf4j-log4j12 175 | 1.7.5 176 | 177 | 178 | 179 | log4j 180 | log4j 181 | 1.2.17 182 | 183 | 184 | 185 | commons-logging 186 | commons-logging 187 | 1.1.3 188 | 189 | 190 | javax.servlet 191 | servlet-api 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | ${project.artifactId} 200 | 201 | 202 | ${basedir}/src/main/resources 203 | false 204 | 205 | 206 | ${basedir}/src/main/filtered 207 | true 208 | 209 | 210 | 211 | 212 | org.apache.maven.plugins 213 | maven-compiler-plugin 214 | 3.1 215 | 216 | 1.8 217 | 218 | 219 | 220 | org.eclipse.jetty 221 | jetty-maven-plugin 222 | 9.1.3.v20140225 223 | 224 | 225 | /${project.artifactId} 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | spring-snapshots 236 | http://repo.spring.io/snapshot 237 | 238 | true 239 | 240 | 241 | false 242 | 243 | 244 | 245 | spring-milestones 246 | http://repo.spring.io/libs-milestone 247 | 248 | false 249 | 250 | 251 | true 252 | 253 | 254 | 255 | java-net 256 | https://maven.java.net/content/repositories/releases 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/shutdownTomcat8.sh: -------------------------------------------------------------------------------- 1 | if [ -z "$TOMCAT8_HOME" ]; then 2 | echo -e "\n\nPlease set TOMCAT8_HOME\n\n" 3 | exit 1 4 | fi 5 | 6 | $TOMCAT8_HOME/bin/shutdown.sh -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/spring-websocket-performance-endpoint.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/filtered/cors.properties: -------------------------------------------------------------------------------- 1 | cors.allow=true 2 | cors.maxage=300000 -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/java/org/cloudmark/samples/performance/dto/Ping.java: -------------------------------------------------------------------------------- 1 | package org.cloudmark.samples.performance.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class Ping { 8 | 9 | @JsonProperty("time") 10 | private long timeInMillis; 11 | @JsonProperty("data") 12 | private String dummyData; 13 | 14 | public Ping(long timeInMillis, final String dummyData){ 15 | this.timeInMillis = timeInMillis; 16 | this.dummyData = dummyData; 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/java/org/cloudmark/samples/performance/dto/User.java: -------------------------------------------------------------------------------- 1 | package org.cloudmark.samples.performance.dto; 2 | 3 | 4 | import lombok.Data; 5 | 6 | @Data 7 | public class User { 8 | private Boolean temporary; 9 | private String username; 10 | } -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/java/org/cloudmark/samples/performance/services/AuthenticationController.java: -------------------------------------------------------------------------------- 1 | package org.cloudmark.samples.performance.services; 2 | 3 | import org.apache.shiro.SecurityUtils; 4 | import org.apache.shiro.authz.annotation.RequiresAuthentication; 5 | import org.apache.shiro.authz.annotation.RequiresGuest; 6 | import org.apache.shiro.subject.Subject; 7 | import org.cloudmark.samples.security.token.SimpleAuthToken; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.RequestHeader; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | import org.springframework.web.bind.annotation.ResponseBody; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | @Controller 20 | public class AuthenticationController { 21 | 22 | private static final Logger logger = LoggerFactory.getLogger(AuthenticationController.class); 23 | 24 | /** 25 | * {@inheritDoc} 26 | */ 27 | @RequiresGuest 28 | @RequestMapping(value = {"/login"}, method = RequestMethod.GET) 29 | public @ResponseBody Map login(@RequestHeader("username") String username, @RequestHeader("password") String password) { 30 | logger.info("Attempting to login user : " + username); 31 | Subject currentUser = SecurityUtils.getSubject(); 32 | SimpleAuthToken casinoEuroToken = new SimpleAuthToken(username, password); 33 | currentUser.login(casinoEuroToken); 34 | return new HashMap(){{ 35 | this.put("session", (String)SecurityUtils.getSubject().getSession().getId()); 36 | }}; 37 | } 38 | 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @RequiresAuthentication 44 | @RequestMapping(value = {"/logout"}, method = RequestMethod.GET, consumes = {"application/json"}, produces = {"application/json"}) 45 | public @ResponseBody Map logout() { 46 | Subject currentUser = SecurityUtils.getSubject(); 47 | logger.info("Attempting to logout user %s", currentUser); 48 | currentUser.logout(); 49 | logger.info(String.format("Successfully logged out User %s", currentUser)); 50 | return new HashMap(){{ 51 | this.put("status", "OK"); 52 | }}; 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/java/org/cloudmark/samples/performance/services/PingController.java: -------------------------------------------------------------------------------- 1 | package org.cloudmark.samples.performance.services; 2 | 3 | import org.apache.commons.lang.RandomStringUtils; 4 | import org.apache.commons.logging.Log; 5 | import org.apache.commons.logging.LogFactory; 6 | import org.cloudmark.samples.performance.dto.Ping; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.ApplicationListener; 10 | import org.springframework.messaging.simp.SimpMessagingTemplate; 11 | import org.springframework.messaging.simp.broker.BrokerAvailabilityEvent; 12 | import org.springframework.scheduling.annotation.Scheduled; 13 | import org.springframework.stereotype.Service; 14 | 15 | import java.util.concurrent.atomic.AtomicBoolean; 16 | 17 | @Service 18 | public class PingController implements ApplicationListener { 19 | private static Log logger = LogFactory.getLog(PingController.class); 20 | 21 | private final SimpMessagingTemplate messagingTemplate; 22 | 23 | private AtomicBoolean brokerAvailable = new AtomicBoolean(); 24 | 25 | @Value("${dummyBytes}") 26 | private Integer fakeDummyData; 27 | 28 | @Autowired 29 | public PingController(SimpMessagingTemplate simpMessagingTemplate) { 30 | this.messagingTemplate = simpMessagingTemplate; 31 | } 32 | 33 | @Scheduled(fixedDelay = 1000) 34 | public void pingEveryOneSecond() { 35 | if (this.brokerAvailable.get()) { 36 | this.messagingTemplate.convertAndSend("/topic/ping.1", createPingMessageResponse()); 37 | } 38 | } 39 | 40 | @Scheduled(fixedDelay = 2000) 41 | public void pingEveryTwoSeconds() { 42 | if (this.brokerAvailable.get()) { 43 | this.messagingTemplate.convertAndSend("/topic/ping.2", createPingMessageResponse()); 44 | } 45 | } 46 | 47 | @Scheduled(fixedDelay = 3000) 48 | public void pingEveryThreeSeconds() { 49 | if (this.brokerAvailable.get()) { 50 | this.messagingTemplate.convertAndSend("/topic/ping.3", createPingMessageResponse()); 51 | } 52 | } 53 | 54 | private Ping createPingMessageResponse(){ 55 | return new Ping(System.currentTimeMillis(), RandomStringUtils.random(fakeDummyData)); 56 | } 57 | 58 | @Override 59 | public void onApplicationEvent(BrokerAvailabilityEvent event) { 60 | this.brokerAvailable.set(event.isBrokerAvailable()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/java/org/cloudmark/samples/security/realm/SimpleAuthorizingRealm.java: -------------------------------------------------------------------------------- 1 | package org.cloudmark.samples.security.realm; 2 | 3 | import org.apache.shiro.SecurityUtils; 4 | import org.apache.shiro.authc.AuthenticationException; 5 | import org.apache.shiro.authc.AuthenticationInfo; 6 | import org.apache.shiro.authc.AuthenticationToken; 7 | import org.apache.shiro.authc.SimpleAuthenticationInfo; 8 | import org.apache.shiro.authz.AuthorizationInfo; 9 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 10 | import org.apache.shiro.authz.UnauthorizedException; 11 | import org.apache.shiro.realm.AuthorizingRealm; 12 | import org.apache.shiro.subject.PrincipalCollection; 13 | import org.apache.shiro.subject.Subject; 14 | import org.apache.shiro.web.util.WebUtils; 15 | import org.cloudmark.samples.security.token.SimpleAuthToken; 16 | import org.springframework.beans.factory.annotation.Qualifier; 17 | import org.springframework.stereotype.Component; 18 | 19 | @Component 20 | @Qualifier("simpleAuthorizingRealm") 21 | public class SimpleAuthorizingRealm extends AuthorizingRealm { 22 | 23 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 24 | Subject subject = SecurityUtils.getSubject(); 25 | if (!WebUtils.isHttp(subject)) throw new UnauthorizedException("Subject argument is not an HTTP-aware instance. "); 26 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); 27 | info.addRole("NORMAL"); 28 | return info; 29 | } 30 | 31 | 32 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 33 | Subject subject = SecurityUtils.getSubject(); 34 | if (!WebUtils.isHttp(subject)) 35 | throw new UnauthorizedException("Subject argument is not an HTTP-aware instance. "); 36 | 37 | SimpleAuthToken casinoEuroToken = (SimpleAuthToken) token; 38 | if (!(casinoEuroToken.getUsername().equals(String.valueOf(casinoEuroToken.getPassword())))) { 39 | throw new UnauthorizedException("The Subject is Unauthorised"); 40 | } 41 | 42 | return new SimpleAuthenticationInfo(casinoEuroToken.getUsername(), casinoEuroToken.getPassword(), getName()); 43 | } 44 | 45 | public boolean supports(AuthenticationToken token) { 46 | return token != null && SimpleAuthToken.class.isAssignableFrom(token.getClass()); 47 | } 48 | 49 | 50 | } -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/java/org/cloudmark/samples/security/token/SimpleAuthToken.java: -------------------------------------------------------------------------------- 1 | package org.cloudmark.samples.security.token; 2 | 3 | import lombok.Data; 4 | import org.apache.shiro.authc.UsernamePasswordToken; 5 | 6 | @Data 7 | public class SimpleAuthToken extends UsernamePasswordToken { 8 | 9 | public SimpleAuthToken(final String username, final String password) { 10 | super(username, password, false, null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/resources/ping.properties: -------------------------------------------------------------------------------- 1 | dummyBytes=512 -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/webapp/WEB-INF/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/webapp/WEB-INF/shiro-secuirty.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/webapp/WEB-INF/web-endpoint-servlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | Cloudmark Web Endpoint 8 | 9 | org.springframework.web.context.ContextLoaderListener 10 | 11 | 12 | 13 | 14 | shiroFilter 15 | org.springframework.web.filter.DelegatingFilterProxy 16 | true 17 | 18 | targetFilterLifecycle 19 | true 20 | 21 | 22 | 23 | 24 | shiroFilter 25 | /* 26 | REQUEST 27 | 28 | 29 | 30 | 31 | web-endpoint 32 | org.springframework.web.servlet.DispatcherServlet 33 | 1 34 | true 35 | 36 | 37 | 38 | web-endpoint 39 | / 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /spring-websocket-performance-endpoint/web-endpoint.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | file://$MODULE_DIR$/../web-endpoint/src/main/webapp/WEB-INF/web-endpoint-servlet.xml 18 | file://$MODULE_DIR$/../web-endpoint/src/main/webapp/WEB-INF/shiro-secuirty.xml 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | --------------------------------------------------------------------------------