Tuesday, July 30, 2013

CPP Queue test


Simple Note

Code

#include <iostream> 
#include <queue>

using namespace std;

/** Test basic operations of queue.
 *
 * From http://www.cplusplus.com/reference/queue/queue/
 *        queues are a type of container adaptor, specifically designed to operate
 *        in a FIFO context (first-in first-out), where elements are inserted into
 *        one end of the container and extracted from the other.
 *
 *        queues are implemented as containers adaptors, which are classes that use
 *        an encapsulated object of a specific container class as its underlying container,
 *        providing a specific set of member functions to access its elements. Elements are
 *        pushed into the "back" of the specific container and popped from its "front".
 *
 * Tested functions:
 * http://www.cplusplus.com/reference/queue/queue/empty/
 * empty: bool empty ( ) const;
 *        Returns whether the queue is empty: i.e. whether its size is zero.
 *
 * http://www.cplusplus.com/reference/queue/queue/size/
 * size: size_type size ( ) const;
 *        Returns the number of elements in the queue.
 *
 * http://www.cplusplus.com/reference/queue/queue/push/
 * push: void push (const value_type& val);
 *        Adds a new element at the end of the queue, after its current last element.
 *        The content of this new element is initialized to val.
 *
 * http://www.cplusplus.com/reference/queue/queue/pop/
 * pop: void pop ( );
 *        Removes the next element in the queue, effectively reducing its size by one.
 *        The element removed is the "oldest" element in the queue whose value can be
 *        retrieved by calling member queue::front.
 *
 *        NOTE: pop probably cause runtime error if queue is empty
 *
 * http://www.cplusplus.com/reference/queue/queue/front/
 * front: value_type& front(); or const value_type& front() const;
 *        Returns a reference to the next element in the queue. This is the "oldest" element in
 *        the queue and the same element that is popped out from the queue when queue::pop is called.
 *
 *        NOTE: front probably cause runtime error if queue is empty
 *
 * http://www.cplusplus.com/reference/queue/queue/back/
 * back: value_type& back(); or const value_type& back() const;
 *        Returns a reference to the last element in the queue. This is the "newest" element in
 *        the queue (i.e. the last element pushed into the queue).
 *
 *        NOTE: back probably cause runtime error if queue is empty
 *
 */

queue<int> testEmptySizePush (queue<int> intQueue);
queue<int> testFrontBack (queue<int> intQueue);
queue<int> testPop (queue<int> intQueue);

int main() {
    queue<int> intQueue;

    intQueue = testEmptySizePush (intQueue);
    intQueue = testFrontBack (intQueue);
    intQueue = testPop (intQueue);

    system("PAUSE");
    return 0;
}
queue<int> testEmptySizePush (queue<int> intQueue) {
    cout << "function testEmptySizePush" << endl;
    // test empty and size
    cout << "is intQueue empty? " << (intQueue.empty()? "true" : "false") << endl;
    cout << "size of intQueue is: " << intQueue.size() << endl;
    cout << "push 3, 5 and 7 into intQueue" << endl;
    // test push
    intQueue.push(3);
    intQueue.push(5);
    intQueue.push(7);
    // test empty and size again
    cout << "is intQueue empty? " << (intQueue.empty()? "true" : "false") << endl;
    cout << "size of intQueue is: " << intQueue.size() << endl << endl;
    return intQueue;
}
queue<int> testFrontBack (queue<int> intQueue) {
    cout << "function testFrontBack" << endl;
    if (!intQueue.empty()) {
        // test front
        cout << "current first value is: " << intQueue.front() << endl;
        cout << "add 5 to first value" << endl;
        intQueue.front() += 5;
        cout << "modified first value is: " << intQueue.front() << endl;

        // test back
        cout << "current last value is: " << intQueue.back() << endl;
        cout << "add 5 to last value" << endl;
        intQueue.back() += 5;
        cout << "modified last value is: " << intQueue.back() << endl << endl;
    }
    return intQueue;
}
queue<int> testPop (queue<int> intQueue) {
    cout << "function testPop" << endl;
    if (!intQueue.empty()) {
        // test pop
        cout << "first value before pop is: " << intQueue.front() << endl;
        cout << "last value before pop is: " << intQueue.back() << endl;
        cout << "pop first value" << endl;
        intQueue.pop();
        cout << "first value after pop is: " << intQueue.front() << endl;
        cout << "last value after pop is: " << intQueue.back() << endl << endl;
    }
    return intQueue;
}


Result



Reference

queue - C++ Reference
http://www.cplusplus.com/reference/queue/queue/

Download

queue.cpp
https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/CPP_Basic_Data_Structures/queue/queue/queue.cpp

Sunday, July 28, 2013

CPP Stack Practice


Simple Note

Code

#include <iostream> 
#include <stack>

using namespace std;

/** Test basic operations of stack.
 *
 * From http://www.cplusplus.com/reference/stack/stack/
 *        Stacks are a type of container adaptor, specifically designed to operate
 *        in a LIFO context (last-in first-out), where elements are inserted and extracted 
 *        only from the end of the container.
 *
 *        stacks are implemented as containers adaptors, which are classes that use an
 *        encapsulated object of a specific container class as its underlying container,
 *        providing a specific set of member functions to access its elements. Elements are
 *        pushed/popped from the "back" of the specific container, which is known as the
 *        top of the stack.
 *
 * Tested functions:
 * http://www.cplusplus.com/reference/stack/stack/empty/
 * empty: bool empty ( ) const;
 *        Returns whether the stack is empty, i.e. whether its size is 0.
 *
 * http://www.cplusplus.com/reference/stack/stack/size/
 * size: size_type size ( ) const;
 *        Returns the number of elements in the stack.
 *
 * http://www.cplusplus.com/reference/stack/stack/push/
 * push: void push ( const T& x );
 *        Adds a new element at the top of the stack, above its current top element.
 *        The content of this new element is initialized to a copy of x.
 *
 *
 * http://www.cplusplus.com/reference/stack/stack/pop/
 * pop: void pop ( );
 *        Removes the element on top of the stack, effectively reducing its size by one.
 *        The value of this element can be retrieved before being popped by calling member stack::top.
 *
 *        NOTE: pop probably cause runtime error if stack is empty
 *
 * http://www.cplusplus.com/reference/stack/stack/top/
 * top: value_type& top ( ); or const value_type& top ( ) const;
 *        Returns a reference to the next element in the stack. Since stacks are last-in first-out
 *        containers this is also the last element pushed into the stack.
 *
 *        NOTE: top probably cause runtime error if stack is empty
 *
 */

stack<int> testEmptySizePush (stack<int> intStack);
stack<int> testTop (stack<int> intStack);
stack<int> testPop (stack<int> intStack);

int main() {
    stack<int> intStack;
    intStack = testEmptySizePush (intStack);
    intStack = testTop (intStack);
    intStack = testPop (intStack);

    system("PAUSE");
    return 0;
}
stack<int> testEmptySizePush (stack<int> intStack) {
    cout << "function testEmptySizePush" << endl;
    // test empty and size
    cout << "is intStack empty? " << (intStack.empty()? "true" : "false") << endl;
    cout << "size of intStack is: " << intStack.size() << endl;
    cout << "push 3 then 5 into intStack" << endl;
    // test push
    intStack.push(3);
    intStack.push(5);
    // test empty and size again
    cout << "is intStack empty? " << (intStack.empty()? "true" : "false") << endl;
    cout << "size of intStack is: " << intStack.size() << endl << endl;
    return intStack;
}
stack<int> testTop (stack<int> intStack) {
    cout << "function testTop" << endl;
    if (!intStack.empty()) {
        // test top
        cout << "current top value is: " << intStack.top() << endl;
        cout << "add 5 to top value" << endl;
        intStack.top() += 5;
        cout << "modified top value is: " << intStack.top() << endl << endl;
    }
    return intStack;
}
stack<int> testPop (stack<int> intStack) {
    cout << "function testPop" << endl;
    if (!intStack.empty()) {
        // test pop
        cout << "top value before pop is: " << intStack.top() << endl;
        cout << "pop top value" << endl;
        intStack.pop();
        cout << "top value after pop is: " << intStack.top() << endl << endl;
    }
    return intStack;
}


Result



Reference

stack - C++ Reference
http://www.cplusplus.com/reference/stack/stack/

Download

stack.cpp
https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/CPP_Basic_Data_Structures/stack/stack.cpp

Saturday, July 27, 2013

CPP Vector STL: Iterator, Size and Capacity


Simple Note

Code

#include <iostream> 
#include <vector>

using namespace std;

/** Test basic vector operation with STL functions,
 *
 * From http://www.cplusplus.com/reference/vector/vector/
 *        Vectors are sequence containers representing arrays that can change in size.
 *
 *        Just like arrays, vectors use contiguous storage locations for their elements,
 *        which means that their elements can also be accessed using offsets on regular pointers
 *        to its elements, and just as efficiently as in arrays. But unlike arrays, their size
 *        can change dynamically, with their storage being handled automatically by the container.
 *
 * Tested functions:
 * http://www.cplusplus.com/reference/vector/vector/begin/
 * begin: iterator begin(); or const_iterator begin() const;
 *        Returns an iterator pointing to the first element in the vector.
 *
 * http://www.cplusplus.com/reference/vector/vector/end/
 * end: iterator end(); or const_iterator end() const;
 *        Returns an iterator referring to the past-the-end element in the vector container.
 *
 * http://www.cplusplus.com/reference/vector/vector/rbegin/
 * rbegin: reverse_iterator rbegin(); or const_reverse_iterator rbegin() const;
 *        Returns a reverse iterator pointing to the last element in the vector
 *        (i.e., its reverse beginning).
 *
 * http://www.cplusplus.com/reference/vector/vector/rend/
 * rend: reverse_iterator rend(); or const_reverse_iterator rend() const;
 *        Returns a reverse iterator pointing to the theoretical element preceding the first
 *        element in the vector (which is considered its reverse end).
 *
 * http://www.cplusplus.com/reference/vector/vector/size/
 * size: size_type size() const;
 *        Returns the number of elements in the vector.
 *        This is the number of actual objects held in the vector, which is
 *        not necessarily equal to its storage capacity.
 *
 * http://www.cplusplus.com/reference/vector/vector/max_size/
 * max_size: size_type max_size() const;
 *        Returns the maximum number of elements that the vector can hold.
 *        This is the maximum potential size the container can reach due to known system or
 *        library implementation limitations, but the container is by no means guaranteed to be able to
 *        reach that size: it can still fail to allocate storage at any point before that size is reached.
 *
 * http://www.cplusplus.com/reference/vector/vector/capacity/
 * capacity: size_type capacity() const;
 *        Returns the size of the storage space currently allocated for the vector,
 *        expressed in terms of elements.
 *        This capacity is not necessarily equal to the vector size. It can be equal or greater,
 *        with the extra space allowing to accommodate for growth without the need to
 *        reallocate on each insertion.
 *
 */
void testIterator ();
void testReverseIterator ();
void testSizeMaxsizeCapacity ();
int main() {
    testIterator ();
    testReverseIterator ();
    testSizeMaxsizeCapacity ();

    system("PAUSE");
    return 0;
}
void testIterator () {
    cout << "function testIterator" << endl;
    vector<int> intVector;
    int cnt = 0;
    for (int i = 0; i < 5; i++) {
        intVector.push_back(i);
    }
    // loop (begin to end) with iterator
    for (vector<int>::iterator it = intVector.begin();
        it != intVector.end();
        it++) {
        if (cnt > 0) {
            cout << ", ";
        }
        cout << *it;
        cnt++;
    }
    cout << endl << endl;
}
void testReverseIterator () {
    cout << "function testReverseIterator" << endl;
    vector<int> intVector;
    int cnt = 0;
    for (int i = 0; i < 5; i++) {
        intVector.push_back(i);
    }
    // loop (end to begin) with reverse iterator
    for (vector<int>::reverse_iterator rit = intVector.rbegin();
        rit != intVector.rend();
        rit++) {
        if (cnt > 0) {
            cout << ", ";
        }
        cout << *rit;
        cnt++;
    }
    cout << endl << endl;
}
void testSizeMaxsizeCapacity () {
    cout << "function testSizeMaxsizeCapacity" << endl;
    vector<int> intVector;

    for (int i = 0; i < 5; i++) {
        intVector.push_back(i);
    }
    // output size, max_size and capacity
    cout << "real size: " << intVector.size() << endl;
    cout << "max size: " << intVector.max_size() << endl;
    cout << "capacity: " << intVector.capacity() << endl;
    cout << endl << endl;
}


Result



Reference

http://www.cplusplus.com/reference/vector/vector/

Download

https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/Vector/vector_STL_iterator_size_capacity.cpp

Saturday, July 20, 2013

CPP Vector STL: Push, Insert and Erase


Simple Note

Code

#include <iostream> 
#include <vector>

using namespace std;

/** Test basic vector operation with STL functions,,
 *
 * From http://www.cplusplus.com/reference/vector/vector/
 *        Vectors are sequence containers representing arrays that can change in size.
 *
 *        Just like arrays, vectors use contiguous storage locations for their elements,
 *        which means that their elements can also be accessed using offsets on regular pointers
 *        to its elements, and just as efficiently as in arrays. But unlike arrays, their size
 *        can change dynamically, with their storage being handled automatically by the container.
 *
 * Tested functions:
 * http://www.cplusplus.com/reference/vector/vector/push_back/
 * push_back: void push_back (const value_type& val);
 *        Adds a new element (val) at the end of the vector
 *
 * http://www.cplusplus.com/reference/vector/vector/insert/
 * insert 1 -- single element: iterator insert (iterator position, const value_type& val);
 *        Insert an element (val) at the specified position
 * insert 2 -- fill: void insert (iterator position, size_type n, const value_type& val);
 *        Fill specified amount (n) of elements (val) starts from specified position
 * insert 3 -- range: template <class InputIterator>
 *            void insert (iterator position, InputIterator first, InputIterator last);
 *        Insert a range (first, last) of vector into this vector,
 *        starts from specified position
 *
 * http://www.cplusplus.com/reference/vector/vector/erase/
 * erase 1 -- erase single element: iterator erase (iterator position);
 *        Erase the element at the specified position
 * erase 2 -- erase a range: iterator erase (iterator first, iterator last);
 *        Erase elements from specified start position (first, included)
 *        to specified end position (last, excluded)
 */
void testBasic ();
void output(vector<int>::iterator start, vector<int>::iterator end, const char* msg);
int main() {
    testBasic();

    system("PAUSE");
    return 0;
}

void testBasic () {
    cout << "function testBasic (push, insert, erase)" << endl;
    vector<int> intVector;
    // {5, 5}
    vector<int> intVectorTwo;
    // push 0, 1, 2 into intVector,
    // intVector will be  {0, 1, 2} after this loop
    for (int i = 0; i < 3; i++) {
        intVector.push_back(i);
    }
    // intVectorTwo becomes {9, 11, 13, 15, 17}
    intVectorTwo.push_back(9);
    intVectorTwo.push_back(11);
    intVectorTwo.push_back(13);
    intVectorTwo.push_back(15);
    intVectorTwo.push_back(17);
    output(intVector.begin(), intVector.end(), "output intVector after push 0, 1, 2: ");
    output(intVectorTwo.begin(), intVectorTwo.end(), "output intVectorTwo: ");
    // insert 3 into intVector at 2nd position,
    // intVector will be  {0, 3, 1, 2}
    intVector.insert(intVector.begin()+1, 3);
    output(intVector.begin(), intVector.end(), "output intVector after insert 3 at 2nd position: ");
    // insert 7 into intVector from 2nd position to 5th position
    // intVector will be {0, 7, 7, 7, 3, 1, 2}
    intVector.insert(intVector.begin()+1, 3, 7);
    output(intVector.begin(), intVector.end(), "output intVector after fill 7 from 2nd position to 4th position: ");
    // insert 3rd element till the element before last element of intVectorTwo
    // to intVector, starts from 4th position of intVector
    // intVector will be {0, 7, 7, 13, 15, 7, 3, 1, 2}
    intVector.insert(intVector.begin()+3, intVectorTwo.begin()+2, intVectorTwo.end()-1);
    output(intVector.begin(), intVector.end(), "output intVector after insert partial intVectorTwo into intVector: ");
    // erase 4th element of intVector
    // intVector will be {0, 7, 7, 15, 7, 3, 1, 2}
    intVector.erase(intVector.begin()+3);
    output(intVector.begin(), intVector.end(), "output intVector after erase 4th element: ");
    // erase 4th element to 6th element of intVector
    // intVector will be {0, 7, 7, 1, 2}
    intVector.erase(intVector.begin()+3, intVector.begin()+6);
    output(intVector.begin(), intVector.end(), "output intVector after erase 4th to 6th elements: ");
    
    cout << endl << endl;
}
void output(vector<int>::iterator start, vector<int>::iterator end, const char* msg) {
    cout << msg << endl;
    int cnt = 0;
    for(; start != end; start++) {
        if (cnt > 0) {
            cout << ", ";
        }
        cout << *start;
        cnt++;
    }
    cout << endl;
}


Result



Reference

http://www.cplusplus.com/reference/vector/vector/

Download

https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/Vector/vector_STL__push_insert_erase.cpp

CPP Vector with Array


Simple Note

Code

#include <iostream> 
#include <vector>

using namespace std;

/** Test basic vector operation with array,
 *
 * From http://www.cplusplus.com/reference/vector/vector/
 *        Vectors are sequence containers representing arrays that can change in size.
 *
 *        Just like arrays, vectors use contiguous storage locations for their elements,
 *        which means that their elements can also be accessed using offsets on regular pointers
 *        to its elements, and just as efficiently as in arrays. But unlike arrays, their size
 *        can change dynamically, with their storage being handled automatically by the container.
 */
void testBasic ();
void testInitialWithArray ();
int main() {
    testBasic();
    testInitialWithArray();
    system("PAUSE");
    return 0;
}

void testBasic () {
    cout << "function testBasic" << endl;
    // declare and init with size and default value
    // first param for size
    // second param for default value
    vector<int> intVector(5, 3);
    // declare and init with another vector
    // the values will be copied from intVector to intVectorTwo
    // change the value of intVectorTwo will not affect intVector
    vector<int> intVectorTwo(intVector);
    // declare int vector
    vector<int> intVectorThree(5);
    intVectorTwo[3] = 5;
    // init
    // vector knows the size itself
    for (int i = 0; i < intVectorThree.size(); i++) {
        intVectorThree[i] = 3*i;
    }
    // output content of vector
    cout << "output initVector: ";
    for (int i = 0; i < intVector.size(); i++) {
        if (i > 0) {
            cout << ", ";
        }
        cout << intVector[i];
    }
    
    cout << endl << "output initVectorTwo: ";
    for (int i = 0; i < intVectorTwo.size(); i++) {
        if (i > 0) {
            cout << ", ";
        }
        cout << intVectorTwo[i];
    }
    cout << endl << "output initVectorThree: ";
    for (int i = 0; i < intVectorThree.size(); i++) {
        if (i > 0) {
            cout << ", ";
        }
        cout << intVectorThree[i];
    }
    cout << endl << endl;
}

void testInitialWithArray () {
    cout << "function testInitialWithArray" << endl;
    int arr[] = {1, 3, 5};
    // declare and init with array
    vector<int> intVector(arr, arr+3);
    // declare and init with partial array
    vector<int> intVectorTwo(arr+1, arr+3);
    // output content of vector
    cout << "output initVector: ";
    for (int i = 0; i < intVector.size(); i++) {
        if (i > 0) {
            cout << ", ";
        }
        cout << intVector[i];
    }
    cout << endl << "output initVectorTwo: ";
    for (int i = 0; i < intVectorTwo.size(); i++) {
        if (i > 0) {
            cout << ", ";
        }
        cout << intVectorTwo[i];
    }
    cout << endl << endl;
}


Result



References

Vector
http://www.cplusplus.com/reference/vector/vector/

Download

https://github.com/benbai123/C_Cplusplus_Practice/blob/master/CPP/CPP_Basic/Vector/vector_with_array.cpp

Sunday, July 14, 2013

Group Connections with WebSocket


Introduction

This article describe how to group connections of WebSocket with server side grouping information.

Environment: Tomcat 7.0.42

Pre-request

Simple WebSocket Test with Tomcat
http://ben-bai.blogspot.tw/2013/07/simple-websocket-test-with-tomcat.html

Result

View demo on line
http://screencast.com/t/uc9VPj2k

Program

index.jsp

Create and trigger WebSocket connection with specified group.

<%@ page language="java"
    contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!-- EL is required -->
<%@ page isELIgnored ="false" %>
<html>
    <head>
        <meta http-equiv="Content-Type" 
            content="text/html; charset=UTF-8"/>
        <title>practice one</title>
        <script type="text/javascript">
            var TestWebSocket = {
                socket: null,
                group: null,
                connect: (function() {
                    // store the specified group
                    TestWebSocket.group = '${GROUP}';
                    // create WebSocket with the specified group
                    var loc = window.location,
                        host = ('ws://' + loc.host + loc.pathname).replace('index.jsp', '') + TestWebSocket.group + '.wsreq';
                    if ('WebSocket' in window) {
                        TestWebSocket.socket = new WebSocket(host);
                    } else if ('MozWebSocket' in window) {
                        TestWebSocket.socket = new MozWebSocket(host);
                    } else {
                        alert('Error: WebSocket is not supported by this browser.');
                        return;
                    }
                    // process message from server
                    TestWebSocket.socket.onmessage = function (message) {
                        document.getElementById('content').innerHTML = message.data;
                    };
                }),
                // send specified group to server
                trigger: (function() {
                    TestWebSocket.socket.send(TestWebSocket.group);
                })
            };

            TestWebSocket.connect();
        </script>
    </head>
    <body>
        Group: ${GROUP}
        <div id="content"></div>
        <button id="btn" onclick="TestWebSocket.trigger();">trigger</button>
    </body>
</html>


TestFilter.java

Used to specify group, update it to client side.

package test;

import java.io.IOException;
import java.util.Random;

import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;

@WebFilter(filterName="TestFilter", urlPatterns={"/index.jsp"})
public class TestFilter implements Filter {
    private static final String[] _groups = {"GroupOne", "GroupTwo", "GroupThree"};
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
             FilterChain chain) throws IOException, ServletException {
        Random r = new Random();
        // put the value "GroupOne"/"GroupTwo"/"GroupThree" with
        // name "GROUP" into request scope
        ((HttpServletRequest)request).setAttribute("GROUP", _groups[r.nextInt(3)]);
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {}
}


TestWebSocketServlet.java

Create server side connections with specified group.

package test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;

/**
 * Tested with Tomcat 7.0.42
 * @author benbai123
 *
 */
public class TestWebSocketServlet extends WebSocketServlet {

    private static final long serialVersionUID = -7663708549630020769L;

    // for message that will be sent to client
    private final AtomicInteger _cntForGroupOne = new AtomicInteger(0);
    private final AtomicInteger _cntForGroupTwo = new AtomicInteger(0);
    private final AtomicInteger _cntForGroupThree = new AtomicInteger(0);
    // hold each connection in this Set
    private final Set<TestMessageInbound> connections =
        new CopyOnWriteArraySet<TestMessageInbound>();
    /**
     * For create connection only, each connection will
     * handle it self as needed
     */
    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol,
            HttpServletRequest request) {
        String uri = request.getRequestURI();
        String group = uri.substring(uri.lastIndexOf("/")+1, uri.length()).replace(".wsreq", "");

        // Create connection with specified group
        return new TestMessageInbound(group);
    }
    private final class TestMessageInbound extends MessageInbound {
        private String _group;
        public TestMessageInbound (String group) {
            _group = group;
        }
        // add self instance into connections Set while opened
        @Override
        protected void onOpen(WsOutbound outbound) {
            connections.add(this);
        }
        // remove self instance from connections Set whild closed
        @Override
        protected void onClose(int status) {
            connections.remove(this);
        }
        // ignore binary message since we just want to process text messages
        @Override
        protected void onBinaryMessage(ByteBuffer message) throws IOException {
            // ignore
        }
        // send a message to each connection in connections Set
        // while receive a text message (specific group here)
        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            send(message.toString());
        }
        public String getGroup () {
            return _group;
        }
    }
    // send message to specific group
    public void send (String group) {
        String msg = "Current count for " + group + ": " + getCounterByGroup(group);;
        for (TestMessageInbound connection : connections) {
            try {
                if (group.equals(connection.getGroup())) {
                    connection.getWsOutbound().writeTextMessage(CharBuffer.wrap(msg));
                }
            } catch (IOException ignore) {
                /* ignore */
            }
        }
    }
    // get message for specific group
    private int getCounterByGroup (String group) {
        return "GroupOne".equals(group)? _cntForGroupOne.incrementAndGet() :
            "GroupTwo".equals(group)? _cntForGroupTwo.incrementAndGet() : _cntForGroupThree.incrementAndGet();
    }
}


web.xml

For servlet mapping

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>TomcatWebSocketTest</display-name>
    <servlet>
        <servlet-name>testWebSocketServlet</servlet-name>
        <servlet-class>test.TestWebSocketServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>testWebSocketServlet</servlet-name>
        <url-pattern>*.wsreq</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>


Download

Full project at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/WebServer/WebSocket/GroupConnectionsWithWebSocket

Demo Flash
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/demo_src/Server/Tomcat/GroupConnectionsWithWebSocket.swf

Saturday, July 13, 2013

ZK: Server Push with WebSocket


Introduction

This article describe how to do Server Push with WebSocket in ZK (this is just a POC with a customized Intbox).

Environment: ZK 6.0.2, Tomcat 7.0.42, Latest Chrome/Firefox.

Result

View demo on line
http://screencast.com/t/8gu7FLic5Ul

Pre-request

ZK Serve Push with Java Timer
http://ben-bai.blogspot.tw/2013/07/zk-serve-push-with-java-timer.html

ZK: Override Widget in zk.xml
http://ben-bai.blogspot.tw/2013/07/zk-override-widget-in-zkxml.html

Simple WebSocket Test with Tomcat
http://ben-bai.blogspot.tw/2013/07/simple-websocket-test-with-tomcat.html

Group Connections with WebSocket
http://ben-bai.blogspot.tw/2013/07/group-connections-with-websocket.html

Program

index.zul

Intbox with custom java class and control buttons.

<zk xmlns:w="client">
    <div apply="test.TestComposer">
        Self:
        <!-- custom intbox,
            use custom java class test.Intbox
            also customize widget in WEB-INF/zk.xml
            support WebSocket
            
            socket context is 'self' denotes does
            not listen to any context,
            for component only -->
        <intbox id="ibx" readonly="true"
            socketContext="self" use="test.Intbox" />
        <!-- button for start server push -->
        <button id="startBtn" label="start" />
        <!-- button for stop server push -->
        <button id="stopBtn" label="stop" />
        <!-- button for show value of intbox 'ibx'
            to make sure both client/server are updated properly -->
        <button label="show value" onClick="alert(ibx.getValue());" />
        <div />
        <!-- custom intbox listen to socket context 'counter' -->
        Counter: <intbox id="ibxCounter" readonly="true"
            socketContext="counter" use="test.Intbox" />
        <div />
        Negative Counter: <intbox id="ibxNegativeCounter" readonly="true"
            socketContext="negativeCounter" use="test.Intbox" />
        <div />
        <!-- button for update value of context 'counter -->
        <button id="updateCounterBtn" label="updaet counter and negative counter" />
        <div />
        <button label="show value of counter" onClick="alert(ibxCounter.getValue());" />
        <button label="show value of negative counter" onClick="alert(ibxNegativeCounter.getValue());" />
        <!-- button for switch context of 'ibxCounter' and 'ibxNegativeCounter'
            write in zul page directly for keeping composer clear
            since this is not the major part -->
        <button label="switch context">
            <attribute name="onClick"><![CDATA[
                String cOne = ibxCounter.getSocketContext();
                String cTwo = ibxNegativeCounter.getSocketContext();
                ibxCounter.setSocketContext(cTwo);
                ibxNegativeCounter.setSocketContext(cOne);
            ]]></attribute>
        </button>
    </div>
</zk>


TestComposer.java

Composer used to handle server push task/action.

package test;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;

import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;

/**
 * Tested with ZK 6.0.2
 * @author benbai123
 *
 */
@SuppressWarnings("rawtypes")
public class TestComposer extends SelectorComposer {

    private static final long serialVersionUID = 5928314519324520566L;
    @Wire
    Intbox ibx;
    private final AtomicInteger _cnt = new AtomicInteger(0);
    private final static AtomicInteger _cntCounter = new AtomicInteger(0);
    private Timer timer;
    /**
     * start server push with WebSocket for
     * specific component 'ibx'
     */
    @Listen("onClick = #startBtn")
    public void start () {
        if (timer == null) {
            timer = new Timer();
            timer.schedule(getTask(), 0, 1000);
        }
    }
    /**
     * stop server push with WebSocket for
     * specific component 'ibx'
     */
    @Listen("onClick = #stopBtn")
    public void stop () {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
    /**
     * update value to context 'counter' and 'negativeCounter' via
     * WebSocket
     * 
     * All components that listen to these context will be updated
     */
    @Listen("onClick = #updateCounterBtn")
    public void updateCounterBtn () {
        String msg = "" + _cntCounter.incrementAndGet();
        TestWebSocketServlet.sendBySocketContext(msg, "counter");
        TestWebSocketServlet.sendBySocketContext("-" + msg, "negativeCounter");
    }
    // task to be scheduled
    public TimerTask getTask () {
        return new TimerTask() {
            public void run () {
                update();
            }
        };
    }
    // update value of intbox 'ibx'
    public void update () {
        ibx.updateSelfValueWithWebSocket(_cnt.getAndIncrement());
    }
}


Intbox.java

Custom Intbox support WebSocket action.

package test;

/**
 * Enhanced Intbox that support WebSocket action
 * @author benbai123
 *
 */
public class Intbox extends org.zkoss.zul.Intbox implements IWebsocketEnhancedComponent {

    private static final long serialVersionUID = 1711581315927992296L;
    /** context used to create socket connection
     * used by custom mapping rule
     */
    private String _socketContext = "";
    private String _mappingId;
    public void setSocketContext (String socketContext) {
        // no need to clear old connection,
        // old connection will be closed at client side
        // then trigger onClose method of connection at
        // server side to clear it
        if (socketContext == null) {
            socketContext = "";
        }
        if (!socketContext.equals(_socketContext)) {
            _socketContext = socketContext;
            // register if has context
            if (!socketContext.isEmpty()) {
                register();
            }
            smartUpdate("socketContext", getMappingContext());
        }
    }
    // getter
    public String getSocketContext () {
        return _socketContext;
    }
    public String getBaseId () {
        return getUuid();
    }
    public String getMappingId () {
        return _mappingId;
    }
    /**
     * append mapping id to context, will parse it in TestWebSocketServlet,
     * @see test.TestWebSocketServlet#createWebSocketInbound(String, javax.servlet.http.HttpServletRequest)
     * @return
     */
    private String getMappingContext () {
        return _socketContext + "_" + _mappingId;
    }
    /**
     * Update value to client side via WebSocket
     * two steps:
     * 1. Set value at server side without any 'update client' action
     * 2. Update value to client side via WebSocket
     * @param value
     * @see test.TestWebSocketServlet#sendByComponent(String, org.zkoss.zk.ui.Component)
     */
    public void updateSelfValueWithWebSocket (int value) {
        setValueDirectly(value);
        TestWebSocketServlet.sendByComponent(value+"", this);
    }

    public void notifyByWebSocket (String msg) {
        setValueDirectly(Integer.parseInt(msg));
    }
    /**
     * Register this component for notify back
     * @see test.TestWebSocketServlet#register(IWebsocketEnhancedComponent)
     * @see test.TestWebSocketServlet#sendBySocketContext(String, String)
     */
    private void register () {
        _mappingId = TestWebSocketServlet.register(this);
    }
    // render socketContext as needed
    protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
        throws java.io.IOException {
        super.renderProperties(renderer);
        if (!"".equals(_socketContext)) {
            render(renderer, "socketContext", getMappingContext());
        }
    }
}


zk.xml

Customize client widget to work with WebSocket.

<zk>
    <device-config>
        <device-type>ajax</device-type>
        <embed><![CDATA[
            <script type="text/javascript">
                zk.afterLoad("zul.inp", function () {
                    var _iwgt = {};
                    zk.override(zul.inp.InputWidget.prototype, _iwgt, {
                        // called when receive message from WebSocket
                        doWebSocketMessage_: function (msg) {
                            // update value of input node
                            jq(this.getInputNode()).val(msg.data);
                        }
                    });
                });
                zk.afterLoad("zul", function () {
                    var _wgt = {};
                    zk.override(zk.Widget.prototype, _wgt, {
                        // setter for set context of WebSocket
                        setSocketContext: function (v) {
                            this._socketContext = v;
                            if (this.$n())
                                this.initWebSocket();
                        },
                        bind_: function (dt, skipper, after) {
                            _wgt.bind_.apply(this, arguments);
                            // initiate WebSocket after bind_
                            this.initWebSocket();
                        },
                        // init WebSocket
                        initWebSocket: function () {
                            // close old at first
                            if (this.TestWebSocket)
                                this.TestWebSocket.disconnect();
                            // create new if a context specified
                            if (this._socketContext) {
                                var wgt = this;
                                this.TestWebSocket = {
                                    socket: null,
                                    connect: (function() {
                                        // .wsreq for servlet mapping
                                        var path = window.location.host + window.location.pathname,
                                            host = 'ws://' + path + wgt._socketContext + '.wsreq';
                                        if ('WebSocket' in window) {
                                            this.socket = new WebSocket(host);
                                        } else if ('MozWebSocket' in window) {
                                            this.socket = new MozWebSocket(host);
                                        } else {
                                            alert('Error: WebSocket is not supported by this browser.');
                                            return;
                                        }
                                        // process message from server
                                        this.socket.onmessage = function (msg) {
                                            wgt.doWebSocketMessage_(msg);
                                        };
                                    }),
                                    disconnect: function () {
                                        // close and clear
                                        this.socket.close();
                                        this.socket = null;
                                        wgt.TestWebSocket = null;
                                    }
                                };
                                
                                this.TestWebSocket.connect();
                            }
                        },
                        doWebSocketMessage_: function (msg) {
                            // for child override
                        }
                    });
                });
            </script>
        ]]></embed>
    </device-config>
</zk>


IWebsocketEnhancedComponent.java

Define a "WebSocket Enhanced Component"

package test;

/**
 * Define a "WebSocket Enhanced Component"
 * @author benbai123
 *
 */
public interface IWebsocketEnhancedComponent {
    /**
     * Called when the listening context is updated
     * @see test.TestWebSocketServlet#sendBySocketContext(String, String)
     */
    public void notifyByWebSocket (String msg);
    /**
     * Base id of this component, components in different sessions probably use the same base id
     * @return
     */
    public String getBaseId ();
    /**
     * Mapping id of this component, should be unique in whole application
     * @return
     */
    public String getMappingId ();
}


TestWebSocketServlet.java

Servlet used to initiate and handle WebSocket connections.

package test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;

/**
 * Tested with Tomcat 7.0.42 and ZK 6.0.2
 * @author benbai123
 *
 */
public class TestWebSocketServlet extends WebSocketServlet {

    private static final long serialVersionUID = -7663708549630020769L;

    // hold each connection
    private static final Set<TestMessageInbound> connections =
        new CopyOnWriteArraySet<TestMessageInbound>();
    // hold each related component
    private static final Map<String, IWebsocketEnhancedComponent> registeredComponents =
        new Hashtable<String, IWebsocketEnhancedComponent>();

    /**
     * For create connection only, each connection will
     * handle it self as needed
     */
    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol,
            HttpServletRequest request) {
        // request uri
        String uri = request.getRequestURI();

        // infos within uri, format is context_mappingId.wsreq e.g., counter_a1234.wsreq
        String infos = uri.substring(uri.lastIndexOf("/")+1, uri.length()).replace(".wsreq", "");
        String context = infos.substring(0, infos.lastIndexOf("_"));
        String compMappingId = infos.substring(infos.lastIndexOf("_")+1, infos.length());
        
        return new TestMessageInbound(context, compMappingId);
    }
    private final class TestMessageInbound extends MessageInbound {
        // hold context and mappingId of related component
        private String _context;
        private String _compMappingId;
        // constructor
        public TestMessageInbound (String context, String compMappingId) {
            _context = context;
            _compMappingId = compMappingId;
        }
        // add self instance into connections Set while opened
        @Override
        protected void onOpen(WsOutbound outbound) {
            connections.add(this);
        }
        // remove self instance from connections set and
        // clear component reference while closed
        @Override
        protected void onClose(int status) {
            connections.remove(this);
            registeredComponents.remove(_compMappingId);
        }
        // ignore binary message
        @Override
        protected void onBinaryMessage(ByteBuffer message) throws IOException {
            // ignore
        }
        // ignore text message
        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            // ignore
        }
        /**
         * get socket context of this connection
         * @return
         */
        public String getContext () {
            return _context;
        }
        /**
         * get mappingId of related component of this connection
         * @return
         */
        public String getCompMappingId () {
            return _compMappingId;
        }
    }

    /**
     * send message via WebSocket with specified socket context
     * all components that connect to this context will be updated
     * @param msg message to send
     * @param socketContext target socket context
     */
    public static void sendBySocketContext (String msg, String socketContext) {
        for (TestMessageInbound connection : connections) {
            
            try {
                // send message to specified socketContext
                // ignore self context and other different context
                if (!"self".equals(connection.getContext())
                    && connection.getContext().equals(socketContext)) {
                    // send message via WebSocket
                    connection.getWsOutbound().writeTextMessage(CharBuffer.wrap(msg));
                    // pass sent message to component
                    // so the component can update itself if needed
                    registeredComponents.get(connection.getCompMappingId()).notifyByWebSocket(msg);
                }
            } catch (IOException ignore) {
                /* ignore */
            }
        }
    }
    /**
     * send message via WebSocket to specific component
     * @param msg message to send
     * @param comp target component
     */
    public static void sendByComponent (String msg, IWebsocketEnhancedComponent comp) {
        for (TestMessageInbound connection : connections) {
            try {
                // send message to specific component
                if (connection.getCompMappingId().equals(comp.getMappingId())) {
                    connection.getWsOutbound().writeTextMessage(CharBuffer.wrap(msg));
                }
            } catch (IOException ignore) {
                /* ignore */
            }
        }
    }
    /**
     * register component so can notify it as needed
     * @param comp component to register
     * @return String, mapping id for specified component
     */
    public static String register (IWebsocketEnhancedComponent comp) {
        String id = comp.getBaseId();
        String mappingId = id;
        int i = 2;
        // check it since components in different sessions probably use the same id
        synchronized (registeredComponents) {
            while (registeredComponents.containsKey(mappingId)) {
                mappingId = id + i;
                i++;
            }
            registeredComponents.put(mappingId, comp);
        }
        return mappingId;
    }
}


web.xml

Define servlet mapping.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0"> 
    <servlet>
        <servlet-name>testWebSocketServlet</servlet-name>
        <servlet-class>test.TestWebSocketServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>testWebSocketServlet</servlet-name>
        <url-pattern>*.wsreq</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.zul</welcome-file>
    </welcome-file-list>
</web-app>


Download

Full project at github
https://github.com/benbai123/ZK_Practice/tree/Tag_for_Component_Based_WebSocket_ServerPush/Pattern/ServerPush/ServerPushWithWebSocket

Demo Flash
https://github.com/benbai123/ZK_Practice/blob/master/demo_src/swf/Pattern/ServerPush/ServerPushWithWebSocket.swf

Friday, July 12, 2013

ZK: Override Widget in zk.xml


Simple Note

Introduction

This article describe how to override a client widget in zk.xml directly, in this way you can do some investigation fast, no need to prepare complex structure at first.

Reference

Client Side Programming
http://books.zkoss.org/wiki/Small_Talks/2010/April/Client_Side_Programming

Download

Basically you can copy WEB-INF/zk.xml and modify the override part as needed.

Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/General/OverrideWidgetInZKXML

Sunday, July 7, 2013

ZK Serve Push with Java Timer


Introduction

This article describe how to use Server Push (long pooling) with java.util.Timer in ZK.

NOTE: This feature require ZK EE.

Result

View demo on line
http://screencast.com/t/CIhJVqw7lgDK

Program

index.zul

Simple zul page.

<zk>
    <div apply="test.TestComposer">
        <intbox id="ibx" readonly="true" />
        <button id="startBtn" label="start" />
        <button id="stopBtn" label="stop" />
    </div>
</zk>


TestComposer.java

Start/stop Server Push with button click.

package test;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;

import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Intbox;
/**
 * Tested with ZK 6.0.2
 * @author benbai123
 *
 */
@SuppressWarnings("rawtypes")
public class TestComposer extends SelectorComposer {

    private static final long serialVersionUID = 5928314519324520566L;
    @Wire
    Intbox ibx;
    private final AtomicInteger _cnt = new AtomicInteger(0);
    private Timer timer;
    @Listen("onClick = #startBtn")
    public void start () {
        final Desktop desktop = Executions.getCurrent().getDesktop();
        if (!desktop.isServerPushEnabled()) {
            // enable server push if not enabled
            desktop.enableServerPush(true);
            timer = new Timer();
            // schedule task
            // getTask() -> get the task that to be scheduled
            // 0 -> no delay of first task, start it immediately
            // 1000 -> delay between each task after first task, 1 sec here
            timer.schedule(getTask(), 0, 1000);
        }
    }
    @Listen("onClick = #stopBtn")
    public void stop () {
        final Desktop desktop = Executions.getCurrent().getDesktop();
        if (desktop.isServerPushEnabled()) {
            // disable server push if enabled
            desktop.enableServerPush(false);
            // cancel scheduled task
            timer.cancel();
        }
    }
    // task to be scheduled
    public TimerTask getTask () {
        return new TimerTask() {
            public void run () {
                update();
            }
        };
    }
    // update value of intbox
    public void update () {
        Desktop desktop = ibx.getDesktop();
        try {
            if(desktop == null) {
                timer.cancel();
                return;
            }
            try {
                // active desktop
                Executions.activate(desktop);
                // update UI
                ibx.setValue(_cnt.getAndIncrement());
            } finally {
                // deactive desktop
                Executions.deactivate(desktop);
            }
        } catch (Exception ignore) {
            /* ignore */
        }
    }
}


Reference

Sample at zkdemo
http://www.zkoss.org/zkdemo/server_push/comet


Download

Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Pattern/ServerPush/ServerPushWithJavaTimer

Demo Flash
https://github.com/benbai123/ZK_Practice/blob/master/demo_src/swf/Pattern/ServerPush/ServerPushWithJavaTimer.swf

Saturday, July 6, 2013

Simple WebSocket Test with Tomcat


Introduction

This article describe how to use HTML5 WebSocket with Tomcat support.

Result

View demo on line
http://screencast.com/t/qyimehPrY

Program

index.html

Initial WebSocket, a button that will trigger WebSocket and display the returned message within content div.

<html>
    <head>
        <script type="text/javascript">
            var TestWebSocket = {
                socket: null,
                connect: (function() {
                    var host = 'ws://' + window.location.host + '/TomcatWebSocketTest/testWebSocketServlet';
                    if ('WebSocket' in window) {
                        TestWebSocket.socket = new WebSocket(host);
                    } else if ('MozWebSocket' in window) {
                        TestWebSocket.socket = new MozWebSocket(host);
                    } else {
                        alert('Error: WebSocket is not supported by this browser.');
                        return;
                    }
                    // process message from server
                    TestWebSocket.socket.onmessage = function (message) {
                        document.getElementById('content').innerHTML = message.data;
                    };
                    /* optional
                    // do something while onopen/onclose if needed
                    TestWebSocket.socket.onopen = function () {
                        alert('WebSocket opened.');
                    };
                    TestWebSocket.socket.onclose = function () {
                        alert('WebSocket closed.');
                    };
                    */
                }),
                // send message to server
                trigger: (function() {
                    TestWebSocket.socket.send('trigger');
                }),
            };

            TestWebSocket.connect();
        </script>
    </head>
    <body>
        <div id="content"></div>
        <button id="btn" onclick="TestWebSocket.trigger();">trigger</button>
    </body>
</html>


TestWebSocketServlet.java

Create and hold each WebSocket StreamInbound instance, where StreamInbound instance will handle receive/response message it self.

package test;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;

/**
 * Tested with Tomcat 7.0.42
 * @author benbai123
 *
 */
public class TestWebSocketServlet extends WebSocketServlet {

    private static final long serialVersionUID = -7663708549630020769L;

    // for message that will be sent to client
    private final AtomicInteger _cnt = new AtomicInteger(0);
    // hold each connection in this Set
    private final Set<TestMessageInbound> connections =
        new CopyOnWriteArraySet<TestMessageInbound>();
    /**
     * For create connection only, each connection will
     * handle it self as needed
     */
    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol,
            HttpServletRequest request) {
        /* enable this fragment to send message by server directly
         * 
        if (_cnt.getAndIncrement() == 0) {
            new Thread(new Runnable(){
                public void run () {
                    try{
                        while (true) {
                            send();
                            Thread.sleep(1000);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        */
        return new TestMessageInbound();
    }
    private final class TestMessageInbound extends MessageInbound {
        // add self instance into connections Set while opened
        @Override
        protected void onOpen(WsOutbound outbound) {
            connections.add(this);
        }
        // remove self instance from connections Set whild closed
        @Override
        protected void onClose(int status) {
            connections.remove(this);
        }
        // ignore binary message since we just want to process text messages
        @Override
        protected void onBinaryMessage(ByteBuffer message) throws IOException {
            throw new UnsupportedOperationException(
                "Unsupported: Binary message.");
        }
        // send a message to each connection in connections Set
        // while receive a text message
        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            send();
        }
    }
    public void send () {
        String msg = "Current count: " + _cnt.getAndIncrement();
        for (TestMessageInbound connection : connections) {
            try {
                connection.getWsOutbound().writeTextMessage(CharBuffer.wrap(msg));
            } catch (IOException ignore) {
                /* ignore */
            }
        }
    }
}


web.xml

Configure WebSocket.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>TomcatWebSocketTest</display-name>
    <servlet>
        <servlet-name>testWebSocketServlet</servlet-name>
        <servlet-class>test.TestWebSocketServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>testWebSocketServlet</servlet-name>
        <url-pattern>/testWebSocketServlet</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>


Reference

Start point of official tutorial.
http://tomcat.apache.org/tomcat-7.0-doc/web-socket-howto.html

Download

Full project at github
https://github.com/benbai123/JSP_Servlet_Practice/tree/master/Practice/WebServer/WebSocket/TomcatWebSocketTest

Demo Flash
https://github.com/benbai123/JSP_Servlet_Practice/blob/master/demo_src/Server/Tomcat/TomcatWebSocketTest.swf