blob: 8d4b95b5fe86617d887c11f250e6145c2b86ec1e [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
var App = require('app');
/**
* Example:
*
* stompClient.connect();
* stompClient.subscribe('topic1', handlerFunc1);
* stompClient.addHandler('topic1', 'handler2-name', handlerFunc2);
* stompClient.removeHandler('topic1', 'handler2-name');
* stompClient.unsubscribe('topic1');
* stompClient.disconnect();
*
*/
module.exports = Em.Object.extend({
/**
* @type {Stomp}
*/
client: null,
/**
* @type {string}
*/
webSocketUrl: '{protocol}://{hostname}{port}/api/stomp/v1/websocket',
/**
* @type {string}
*/
sockJsUrl: '{protocol}://{hostname}{port}/api/stomp/v1',
/**
* sockJs should use only alternative options as transport in case when websocket supported but connection fails
* @const
* @type {Array}
*/
sockJsTransports: ['eventsource', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling'],
/**
* @type {boolean}
*/
isConnected: false,
/**
* @type {boolean}
*/
isWebSocketSupported: true,
/**
* @type {number}
* @const
*/
RECONNECT_TIMEOUT: 6000,
/**
* @type {object}
*/
subscriptions: {},
/**
* default headers
* @type {object}
*/
headers: {},
/**
*
* @param {boolean} useSockJS
* @returns {$.Deferred}
*/
connect: function(useSockJS) {
const dfd = $.Deferred();
const socket = this.getSocket(useSockJS);
const client = Stomp.over(socket);
const headers = this.get('headers');
client.connect(headers, () => {
this.onConnectionSuccess();
dfd.resolve();
}, () => {
dfd.reject(this.onConnectionError(useSockJS));
});
client.debug = Em.K;
this.set('client', client);
return dfd.promise();
},
/**
*
* @param {boolean} useSockJS
* @returns {SockJS|WebSocket}
*/
getSocket: function(useSockJS) {
if (!WebSocket || useSockJS) {
this.set('isWebSocketSupported', false);
const sockJsUrl = this.getSocketUrl(this.get('sockJsUrl'), false);
return new SockJS(sockJsUrl, null, {transports: this.get('sockJsTransports')});
} else {
return new WebSocket(this.getSocketUrl(this.get('webSocketUrl'), true));
}
},
/**
*
* @param {string} template
* @param {boolean} isWebsocket
* @returns {string}
*/
getSocketUrl: function(template, isWebsocket) {
const hostname = this.getHostName();
const isSecure = this.isSecure();
const protocol = isWebsocket ? (isSecure ? 'wss' : 'ws') : (isSecure ? 'https' : 'http');
const port = this.getPort();
return template.replace('{hostname}', hostname).replace('{protocol}', protocol).replace('{port}', port);
},
getHostName: function () {
return window.location.hostname;
},
isSecure: function () {
return window.location.protocol === 'https:';
},
getPort: function () {
return window.location.port ? (':' + window.location.port) : '';
},
onConnectionSuccess: function() {
this.set('isConnected', true);
},
onConnectionError: function(useSockJS) {
if (this.get('isConnected')) {
this.reconnect(useSockJS);
} else if (!useSockJS) {//if SockJs connection failed too the stop trying to connect
//if webSocket failed on initial connect then switch to SockJS
return this.connect(true);
}
},
reconnect: function(useSockJS) {
const subscriptions = Object.assign({}, this.get('subscriptions'));
setTimeout(() => {
console.debug('Reconnecting to WebSocket...');
this.connect(useSockJS).done(() => {
this.set('subscriptions', {});
for (let i in subscriptions) {
this.subscribe(subscriptions[i].destination, subscriptions[i].handlers['default']);
for (let key in subscriptions[i].handlers) {
key !== 'default' && this.addHandler(subscriptions[i].destination, key, subscriptions[i].handlers[key]);
}
}
});
}, this.RECONNECT_TIMEOUT);
},
disconnect: function () {
this.get('client').disconnect();
},
/**
*
* @param {string} destination
* @param {string} body
* @param {object} headers
*/
send: function(destination, body, headers = {}) {
if (this.get('client.connected')) {
this.get('client').send(destination, headers, body);
return true;
}
return false;
},
/**
*
* @param destination
* @param {function} handler
* @returns {*}
*/
subscribe: function(destination, handler = Em.K) {
const handlers = {
default: handler
};
if (!this.get('client.connected')) {
return null;
}
if (this.get('subscriptions')[destination]) {
console.error(`Subscription with default handler for ${destination} already exists`);
return this.get('subscriptions')[destination];
} else {
const subscription = this.get('client').subscribe(destination, (message) => {
for (const i in handlers) {
handlers[i](JSON.parse(message.body));
}
});
subscription.destination = destination;
subscription.handlers = handlers;
this.get('subscriptions')[destination] = subscription;
return subscription;
}
},
/**
* If trying to add handler to not existing subscription then it will be created and handler added as default
* @param {string} destination
* @param {string} key
* @param {function} handler
*/
addHandler: function(destination, key, handler) {
const subscription = this.get('subscriptions')[destination];
if (!subscription) {
this.subscribe(destination);
return this.addHandler(destination, key, handler);
}
if (subscription.handlers[key]) {
console.error('You can\'t override subscription handler');
return;
}
subscription.handlers[key] = handler;
},
/**
* If removed handler is last and subscription have zero handlers then topic will be unsubscribed
* @param {string} destination
* @param {string} key
*/
removeHandler: function(destination, key) {
const subscription = this.get('subscriptions')[destination];
delete subscription.handlers[key];
if (Em.keys(subscription.handlers).length === 0) {
this.unsubscribe(destination);
}
},
/**
*
* @param {string} destination
* @returns {boolean}
*/
unsubscribe: function(destination) {
if (this.get('subscriptions')[destination]) {
this.get('subscriptions')[destination].unsubscribe();
delete this.get('subscriptions')[destination];
return true;
}
return false;
}
});