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 +## +## [email protected] +## 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>
