You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ao...@apache.org on 2017/04/06 09:34:01 UTC

[40/41] ambari git commit: AMBARI-20684. Implement a websocket adapter for stomp.py (aonishuk)

http://git-wip-us.apache.org/repos/asf/ambari/blob/8de3961b/ambari-common/src/main/python/ambari_ws4py/utf8validator.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/ambari_ws4py/utf8validator.py b/ambari-common/src/main/python/ambari_ws4py/utf8validator.py
new file mode 100644
index 0000000..50b19e5
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_ws4py/utf8validator.py
@@ -0,0 +1,117 @@
+# coding=utf-8
+
+###############################################################################
+##
+##  Copyright 2011 Tavendo GmbH
+##
+##  Note:
+##
+##  This code is a Python implementation of the algorithm
+##
+##            "Flexible and Economical UTF-8 Decoder"
+##
+##  by Bjoern Hoehrmann
+##
+##       bjoern@hoehrmann.de
+##       http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+##
+##  Licensed under the Apache License, Version 2.0 (the "License");
+##  you may not use this file except in compliance with the License.
+##  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+##  Unless required by applicable law or agreed to in writing, software
+##  distributed under the License is distributed on an "AS IS" BASIS,
+##  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+##  See the License for the specific language governing permissions and
+##  limitations under the License.
+##
+###############################################################################
+
+
+class Utf8Validator(object):
+    """
+    Incremental UTF-8 validator with constant memory consumption (minimal state).
+
+    Implements the algorithm "Flexible and Economical UTF-8 Decoder" by
+    Bjoern Hoehrmann (http://bjoern.hoehrmann.de/utf-8/decoder/dfa/).
+    """
+
+    ## DFA transitions
+    UTF8VALIDATOR_DFA = [
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 00..1f
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 20..3f
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 40..5f
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 60..7f
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, # 80..9f
+        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, # a0..bf
+        8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, # c0..df
+        0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, # e0..ef
+        0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, # f0..ff
+        0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, # s0..s0
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, # s1..s2
+        1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, # s3..s4
+        1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, # s5..s6
+        1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, # s7..s8
+    ]
+
+    UTF8_ACCEPT = 0
+    UTF8_REJECT = 1
+
+    def __init__(self):
+        self.reset()
+
+    def decode(self, b):
+        """
+        Eat one UTF-8 octet, and validate on the fly.
+
+        Returns UTF8_ACCEPT when enough octets have been consumed, in which case
+        self.codepoint contains the decoded Unicode code point.
+
+        Returns UTF8_REJECT when invalid UTF-8 was encountered.
+
+        Returns some other positive integer when more octets need to be eaten.
+        """
+        type = Utf8Validator.UTF8VALIDATOR_DFA[b]
+        if self.state != Utf8Validator.UTF8_ACCEPT:
+            self.codepoint = (b & 0x3f) | (self.codepoint << 6)
+        else:
+            self.codepoint = (0xff >> type) & b
+        self.state = Utf8Validator.UTF8VALIDATOR_DFA[256 + self.state * 16 + type]
+        return self.state
+
+    def reset(self):
+        """
+        Reset validator to start new incremental UTF-8 decode/validation.
+        """
+        self.state = Utf8Validator.UTF8_ACCEPT
+        self.codepoint = 0
+        self.i = 0
+
+    def validate(self, ba):
+        """
+        Incrementally validate a chunk of bytes provided as bytearray.
+
+        Will return a quad (valid?, endsOnCodePoint?, currentIndex, totalIndex).
+
+        As soon as an octet is encountered which renders the octet sequence
+        invalid, a quad with valid? == False is returned. currentIndex returns
+        the index within the currently consumed chunk, and totalIndex the
+        index within the total consumed sequence that was the point of bail out.
+        When valid? == True, currentIndex will be len(ba) and totalIndex the
+        total amount of consumed bytes.
+        """
+        state = self.state
+        DFA = Utf8Validator.UTF8VALIDATOR_DFA
+        i = 0  # make sure 'i' is set if when 'ba' is empty
+        for i, b in enumerate(ba):
+            ## optimized version of decode(), since we are not interested in actual code points
+            state = DFA[256 + (state << 4) + DFA[b]]
+            if state == Utf8Validator.UTF8_REJECT:
+                self.i += i
+                self.state = state
+                return False, False, i, self.i
+        self.i += i
+        self.state = state
+        return True, state == Utf8Validator.UTF8_ACCEPT, i, self.i

http://git-wip-us.apache.org/repos/asf/ambari/blob/8de3961b/ambari-common/src/main/python/ambari_ws4py/websocket.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/ambari_ws4py/websocket.py b/ambari-common/src/main/python/ambari_ws4py/websocket.py
new file mode 100644
index 0000000..b5c1fd3
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_ws4py/websocket.py
@@ -0,0 +1,535 @@
+# -*- coding: utf-8 -*-
+import logging
+import socket
+import ssl
+import time
+import threading
+import types
+import errno
+
+try:
+    from OpenSSL.SSL import Error as pyOpenSSLError
+except ImportError:
+    class pyOpenSSLError(Exception):
+        pass
+
+from ambari_ws4py import WS_KEY, WS_VERSION
+from ambari_ws4py.exc import HandshakeError, StreamClosed
+from ambari_ws4py.streaming import Stream
+from ambari_ws4py.messaging import Message, PingControlMessage,\
+    PongControlMessage
+from ambari_ws4py.compat import basestring, unicode
+
+DEFAULT_READING_SIZE = 2
+
+logger = logging.getLogger('ambari_ws4py')
+
+__all__ = ['WebSocket', 'EchoWebSocket', 'Heartbeat']
+
+class Heartbeat(threading.Thread):
+    def __init__(self, websocket, frequency=2.0):
+        """
+        Runs at a periodic interval specified by
+        `frequency` by sending an unsolicitated pong
+        message to the connected peer.
+
+        If the message fails to be sent and a socket
+        error is raised, we close the websocket
+        socket automatically, triggering the `closed`
+        handler.
+        """
+        threading.Thread.__init__(self)
+        self.websocket = websocket
+        self.frequency = frequency
+
+    def __enter__(self):
+        if self.frequency:
+            self.start()
+        return self
+
+    def __exit__(self, exc_type, exc_value, exc_tb):
+        self.stop()
+
+    def stop(self):
+        self.running = False
+
+    def run(self):
+        self.running = True
+        while self.running:
+            time.sleep(self.frequency)
+            if self.websocket.terminated:
+                break
+
+            try:
+                self.websocket.send(PongControlMessage(data='beep'))
+            except socket.error:
+                logger.info("Heartbeat failed")
+                self.websocket.server_terminated = True
+                self.websocket.close_connection()
+                break
+
+class WebSocket(object):
+    """ Represents a websocket endpoint and provides a high level interface to drive the endpoint. """
+
+    def __init__(self, sock, protocols=None, extensions=None, environ=None, heartbeat_freq=None):
+        """ The ``sock`` is an opened connection
+        resulting from the websocket handshake.
+
+        If ``protocols`` is provided, it is a list of protocols
+        negotiated during the handshake as is ``extensions``.
+
+        If ``environ`` is provided, it is a copy of the WSGI environ
+        dictionnary from the underlying WSGI server.
+        """
+
+        self.stream = Stream(always_mask=False)
+        """
+        Underlying websocket stream that performs the websocket
+        parsing to high level objects. By default this stream
+        never masks its messages. Clients using this class should
+        set the ``stream.always_mask`` fields to ``True``
+        and ``stream.expect_masking`` fields to ``False``.
+        """
+
+        self.protocols = protocols
+        """
+        List of protocols supported by this endpoint.
+        Unused for now.
+        """
+
+        self.extensions = extensions
+        """
+        List of extensions supported by this endpoint.
+        Unused for now.
+        """
+
+        self.sock = sock
+        """
+        Underlying connection.
+        """
+
+        self._is_secure = hasattr(sock, '_ssl') or hasattr(sock, '_sslobj')
+        """
+        Tell us if the socket is secure or not.
+        """
+
+        self.client_terminated = False
+        """
+        Indicates if the client has been marked as terminated.
+        """
+
+        self.server_terminated = False
+        """
+        Indicates if the server has been marked as terminated.
+        """
+
+        self.reading_buffer_size = DEFAULT_READING_SIZE
+        """
+        Current connection reading buffer size.
+        """
+
+        self.environ = environ
+        """
+        WSGI environ dictionary.
+        """
+
+        self.heartbeat_freq = heartbeat_freq
+        """
+        At which interval the heartbeat will be running.
+        Set this to `0` or `None` to disable it entirely.
+        """
+        "Internal buffer to get around SSL problems"
+        self.buf = b''
+
+        self._local_address = None
+        self._peer_address = None
+
+    @property
+    def local_address(self):
+        """
+        Local endpoint address as a tuple
+        """
+        if not self._local_address:
+            self._local_address = self.sock.getsockname()
+            if len(self._local_address) == 4:
+                self._local_address = self._local_address[:2]
+        return self._local_address
+
+    @property
+    def peer_address(self):
+        """
+        Peer endpoint address as a tuple
+        """
+        if not self._peer_address:
+            self._peer_address = self.sock.getpeername()
+            if len(self._peer_address) == 4:
+                self._peer_address = self._peer_address[:2]
+        return self._peer_address
+
+    def opened(self):
+        """
+        Called by the server when the upgrade handshake
+        has succeeded.
+        """
+        pass
+
+    def close(self, code=1000, reason=''):
+        """
+        Call this method to initiate the websocket connection
+        closing by sending a close frame to the connected peer.
+        The ``code`` is the status code representing the
+        termination's reason.
+
+        Once this method is called, the ``server_terminated``
+        attribute is set. Calling this method several times is
+        safe as the closing frame will be sent only the first
+        time.
+
+        .. seealso:: Defined Status Codes http://tools.ietf.org/html/rfc6455#section-7.4.1
+        """
+        if not self.server_terminated:
+            self.server_terminated = True
+            try:
+                self._write(self.stream.close(code=code, reason=reason).single(mask=self.stream.always_mask))
+            except Exception as ex:
+                logger.error("Error when terminating the connection: %s", str(ex))
+
+    def closed(self, code, reason=None):
+        """
+        Called  when the websocket stream and connection are finally closed.
+        The provided ``code`` is status set by the other point and
+        ``reason`` is a human readable message.
+
+        .. seealso:: Defined Status Codes http://tools.ietf.org/html/rfc6455#section-7.4.1
+        """
+        pass
+
+    @property
+    def terminated(self):
+        """
+        Returns ``True`` if both the client and server have been
+        marked as terminated.
+        """
+        return self.client_terminated is True and self.server_terminated is True
+
+    @property
+    def connection(self):
+        return self.sock
+
+    def close_connection(self):
+        """
+        Shutdowns then closes the underlying connection.
+        """
+        if self.sock:
+            try:
+                self.sock.shutdown(socket.SHUT_RDWR)
+                self.sock.close()
+            except:
+                pass
+            finally:
+                self.sock = None
+
+    def ping(self, message):
+        """
+        Send a ping message to the remote peer.
+        The given `message` must be a unicode string.
+        """
+        self.send(PingControlMessage(message))
+
+    def ponged(self, pong):
+        """
+        Pong message, as a :class:`messaging.PongControlMessage` instance,
+        received on the stream.
+        """
+        pass
+
+    def received_message(self, message):
+        """
+        Called whenever a complete ``message``, binary or text,
+        is received and ready for application's processing.
+
+        The passed message is an instance of :class:`messaging.TextMessage`
+        or :class:`messaging.BinaryMessage`.
+
+        .. note:: You should override this method in your subclass.
+        """
+        pass
+
+    def unhandled_error(self, error):
+        """
+        Called whenever a socket, or an OS, error is trapped
+        by ambari_ws4py but not managed by it. The given error is
+        an instance of `socket.error` or `OSError`.
+
+        Note however that application exceptions will not go
+        through this handler. Instead, do make sure you
+        protect your code appropriately in `received_message`
+        or `send`.
+
+        The default behaviour of this handler is to log
+        the error with a message.
+        """
+        logger.exception("Failed to receive data")
+
+    def _write(self, b):
+        """
+        Trying to prevent a write operation
+        on an already closed websocket stream.
+
+        This cannot be bullet proof but hopefully
+        will catch almost all use cases.
+        """
+        if self.terminated or self.sock is None:
+            raise RuntimeError("Cannot send on a terminated websocket")
+
+        self.sock.sendall(b)
+
+    def send(self, payload, binary=False):
+        """
+        Sends the given ``payload`` out.
+
+        If ``payload`` is some bytes or a bytearray,
+        then it is sent as a single message not fragmented.
+
+        If ``payload`` is a generator, each chunk is sent as part of
+        fragmented message.
+
+        If ``binary`` is set, handles the payload as a binary message.
+        """
+        message_sender = self.stream.binary_message if binary else self.stream.text_message
+
+        if isinstance(payload, basestring) or isinstance(payload, bytearray):
+            m = message_sender(payload).single(mask=self.stream.always_mask)
+            self._write(m)
+
+        elif isinstance(payload, Message):
+            data = payload.single(mask=self.stream.always_mask)
+            self._write(data)
+
+        elif type(payload) == types.GeneratorType:
+            bytes = next(payload)
+            first = True
+            for chunk in payload:
+                self._write(message_sender(bytes).fragment(first=first, mask=self.stream.always_mask))
+                bytes = chunk
+                first = False
+
+            self._write(message_sender(bytes).fragment(first=first, last=True, mask=self.stream.always_mask))
+
+        else:
+            raise ValueError("Unsupported type '%s' passed to send()" % type(payload))
+
+    def _get_from_pending(self):
+        """
+        The SSL socket object provides the same interface
+        as the socket interface but behaves differently.
+
+        When data is sent over a SSL connection
+        more data may be read than was requested from by
+        the ambari_ws4py websocket object.
+
+        In that case, the data may have been indeed read
+        from the underlying real socket, but not read by the
+        application which will expect another trigger from the
+        manager's polling mechanism as if more data was still on the
+        wire. This will happen only when new data is
+        sent by the other peer which means there will be
+        some delay before the initial read data is handled
+        by the application.
+
+        Due to this, we have to rely on a non-public method
+        to query the internal SSL socket buffer if it has indeed
+        more data pending in its buffer.
+
+        Now, some people in the Python community
+        `discourage <https://bugs.python.org/issue21430>`_
+        this usage of the ``pending()`` method because it's not
+        the right way of dealing with such use case. They advise
+        `this approach <https://docs.python.org/dev/library/ssl.html#notes-on-non-blocking-sockets>`_
+        instead. Unfortunately, this applies only if the
+        application can directly control the poller which is not
+        the case with the WebSocket abstraction here.
+
+        We therefore rely on this `technic <http://stackoverflow.com/questions/3187565/select-and-ssl-in-python>`_
+        which seems to be valid anyway.
+
+        This is a bit of a shame because we have to process
+        more data than what wanted initially.
+        """
+        data = b""
+        pending = self.sock.pending()
+        while pending:
+            data += self.sock.recv(pending)
+            pending = self.sock.pending()
+        return data
+
+    def once(self):
+        """
+        Performs the operation of reading from the underlying
+        connection in order to feed the stream of bytes.
+
+        Because this needs to support SSL sockets, we must always
+        read as much as might be in the socket at any given time,
+        however process expects to have itself called with only a certain
+        number of bytes at a time. That number is found in
+        self.reading_buffer_size, so we read everything into our own buffer,
+        and then from there feed self.process.
+
+        Then the stream indicates
+        whatever size must be read from the connection since
+        it knows the frame payload length.
+
+        It returns `False` if an error occurred at the
+        socket level or during the bytes processing. Otherwise,
+        it returns `True`.
+        """
+        if self.terminated:
+            logger.debug("WebSocket is already terminated")
+            return False
+        try:
+            b = self.sock.recv(self.reading_buffer_size)
+            if self._is_secure:
+                b += self._get_from_pending()
+            if not b:
+                return False
+            self.buf += b
+        except (socket.error, OSError, pyOpenSSLError) as e:
+            if hasattr(e, "errno") and e.errno == errno.EINTR:
+                pass
+            else:
+                self.unhandled_error(e)
+                return False
+        else:
+            # process as much as we can
+            # the process will stop either if there is no buffer left
+            # or if the stream is closed
+            if not self.process(self.buf):
+                return False
+            self.buf = b""
+
+        return True
+
+    def terminate(self):
+        """
+        Completes the websocket by calling the `closed`
+        method either using the received closing code
+        and reason, or when none was received, using
+        the special `1006` code.
+
+        Finally close the underlying connection for
+        good and cleanup resources by unsetting
+        the `environ` and `stream` attributes.
+        """
+        s = self.stream
+
+        try:
+            if s.closing is None:
+                self.closed(1006, "Going away")
+            else:
+                self.closed(s.closing.code, s.closing.reason)
+        finally:
+            self.client_terminated = self.server_terminated = True
+            self.close_connection()
+
+            # Cleaning up resources
+            s._cleanup()
+            self.stream = None
+            self.environ = None
+
+    def process(self, bytes):
+        """ Takes some bytes and process them through the
+        internal stream's parser. If a message of any kind is
+        found, performs one of these actions:
+
+        * A closing message will initiate the closing handshake
+        * Errors will initiate a closing handshake
+        * A message will be passed to the ``received_message`` method
+        * Pings will see pongs be sent automatically
+        * Pongs will be passed to the ``ponged`` method
+
+        The process should be terminated when this method
+        returns ``False``.
+        """
+        s = self.stream
+
+        if not bytes and self.reading_buffer_size > 0:
+            return False
+
+        self.reading_buffer_size = s.parser.send(bytes) or DEFAULT_READING_SIZE
+
+        if s.closing is not None:
+            logger.debug("Closing message received (%d) '%s'" % (s.closing.code, s.closing.reason))
+            if not self.server_terminated:
+                self.close(s.closing.code, s.closing.reason)
+            else:
+                self.client_terminated = True
+            return False
+
+        if s.errors:
+            for error in s.errors:
+                logger.debug("Error message received (%d) '%s'" % (error.code, error.reason))
+                self.close(error.code, error.reason)
+            s.errors = []
+            return False
+
+        if s.has_message:
+            self.received_message(s.message)
+            if s.message is not None:
+                s.message.data = None
+                s.message = None
+            return True
+
+        if s.pings:
+            for ping in s.pings:
+                self._write(s.pong(ping.data))
+            s.pings = []
+
+        if s.pongs:
+            for pong in s.pongs:
+                self.ponged(pong)
+            s.pongs = []
+
+        return True
+
+    def run(self):
+        """
+        Performs the operation of reading from the underlying
+        connection in order to feed the stream of bytes.
+
+        We start with a small size of two bytes to be read
+        from the connection so that we can quickly parse an
+        incoming frame header. Then the stream indicates
+        whatever size must be read from the connection since
+        it knows the frame payload length.
+
+        Note that we perform some automatic opererations:
+
+        * On a closing message, we respond with a closing
+          message and finally close the connection
+        * We respond to pings with pong messages.
+        * Whenever an error is raised by the stream parsing,
+          we initiate the closing of the connection with the
+          appropiate error code.
+
+        This method is blocking and should likely be run
+        in a thread.
+        """
+        self.sock.setblocking(True)
+        with Heartbeat(self, frequency=self.heartbeat_freq):
+            s = self.stream
+
+            try:
+                self.opened()
+                while not self.terminated:
+                    if not self.once():
+                        break
+            finally:
+                self.terminate()
+
+class EchoWebSocket(WebSocket):
+    def received_message(self, message):
+        """
+        Automatically sends back the provided ``message`` to
+        its originating endpoint.
+        """
+        self.send(message.data, message.is_binary)

http://git-wip-us.apache.org/repos/asf/ambari/blob/8de3961b/ambari-project/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-project/pom.xml b/ambari-project/pom.xml
index 98da9f4..5333d9d 100644
--- a/ambari-project/pom.xml
+++ b/ambari-project/pom.xml
@@ -30,7 +30,7 @@
     <solr.version>5.5.2</solr.version>
     <ambari.dir>${project.parent.basedir}</ambari.dir>
     <powermock.version>1.6.3</powermock.version>
-    <jetty.version>8.1.19.v20160209</jetty.version>
+    <jetty.version>9.4.2.v20170220</jetty.version>
     <checkstyle.version>6.19</checkstyle.version> <!-- last version that does not require Java 8 -->
     <forkCount>4</forkCount>
     <reuseForks>false</reuseForks>
@@ -123,17 +123,17 @@
       <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-core</artifactId>
-        <version>3.1.2.RELEASE</version>
+        <version>4.2.2.RELEASE</version>
       </dependency>
       <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-config</artifactId>
-        <version>3.1.2.RELEASE</version>
+        <version>4.2.2.RELEASE</version>
       </dependency>
       <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-web</artifactId>
-        <version>3.1.2.RELEASE</version>
+        <version>4.2.2.RELEASE</version>
       </dependency>
       <dependency>
         <groupId>org.springframework.security.kerberos</groupId>
@@ -293,6 +293,11 @@
         <version>${jetty.version}</version>
       </dependency>
       <dependency>
+        <groupId>org.eclipse.jetty</groupId>
+        <artifactId>jetty-util-ajax</artifactId>
+        <version>${jetty.version}</version>
+      </dependency>
+      <dependency>
         <groupId>org.mortbay.jetty</groupId>
         <artifactId>jsp-api-2.1-glassfish</artifactId>
         <version>2.1.v20100127</version>
@@ -303,6 +308,16 @@
         <version>2.1.v20100127</version>
       </dependency>
       <dependency>
+        <groupId>org.eclipse.jetty.websocket</groupId>
+        <artifactId>websocket-servlet</artifactId>
+        <version>${jetty.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.eclipse.jetty.websocket</groupId>
+        <artifactId>websocket-server</artifactId>
+        <version>${jetty.version}</version>
+      </dependency>
+      <dependency>
         <groupId>org.apache.ant</groupId>
         <artifactId>ant</artifactId>
         <version>1.7.1</version>
@@ -415,6 +430,83 @@
         <version>1.19</version>
       </dependency>
       <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-aop</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-jdbc</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-core</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-context</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-context-support</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-web</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-websocket</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-messaging</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-webmvc</artifactId>
+        <version>4.3.7.RELEASE</version>
+      </dependency>
+      <dependency>
+        <groupId>com.sun.jersey.contribs</groupId>
+        <artifactId>jersey-spring</artifactId>
+        <version>1.19</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aop</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <dependency>
         <groupId>log4j</groupId>
         <artifactId>log4j</artifactId>
         <version>1.2.17</version>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8de3961b/ambari-server/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml
index 8b4c8d6..7fc62f2 100644
--- a/ambari-server/pom.xml
+++ b/ambari-server/pom.xml
@@ -1206,6 +1206,10 @@
       <version>${jetty.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-util-ajax</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.mortbay.jetty</groupId>
       <artifactId>jsp-api-2.1-glassfish</artifactId>
     </dependency>
@@ -1221,7 +1225,14 @@
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-server</artifactId>
-      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-servlet</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty.websocket</groupId>
+      <artifactId>websocket-server</artifactId>
     </dependency>
     <dependency>
       <groupId>commons-logging</groupId>
@@ -1301,6 +1312,75 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-aop</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-jdbc</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-web</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-websocket</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-messaging</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-webmvc</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.sun.jersey.contribs</groupId>
+      <artifactId>jersey-spring</artifactId>
+      <version>1.19</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.springframework</groupId>
+          <artifactId>spring</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.springframework</groupId>
+          <artifactId>spring-core</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.springframework</groupId>
+          <artifactId>spring-web</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.springframework</groupId>
+          <artifactId>spring-beans</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.springframework</groupId>
+          <artifactId>spring-context</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.springframework</groupId>
+          <artifactId>spring-aop</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
       <groupId>com.sun.jersey.jersey-test-framework</groupId>
       <artifactId>jersey-test-framework-grizzly2</artifactId>
       <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8de3961b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index f192895..e5cd709 100644
--- a/pom.xml
+++ b/pom.xml
@@ -306,6 +306,8 @@
             <exclude>ambari-common/src/main/python/ambari_simplejson/**</exclude>
             <!--Stomp library (Apache license)-->
             <exclude>ambari-common/src/main/python/ambari_stomp/**</exclude>
+            <!--ws4py library (BSD 3-Clause)-->
+            <exclude>ambari-common/src/main/python/ambari_ws4py/**</exclude>
 
             <exclude>ambari-web/node_modules/**</exclude>