1 /*
  2  ****************************************************************
  3  * Licensed Materials - Property of IBM
  4  * 5725-S17 IBM MessageSight
  5  * (C) Copyright IBM Corp.  2014, 2015.  All Rights Reserved.
  6  *
  7  * US Government Users Restricted Rights - Use, duplication or
  8  * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  9  ****************************************************************
 10  */
 11 MessagingREST = (function (global) {
 12 	var version = "2.0";
 13 	
 14 	/** 
 15 	 * The JavaScript application communicates with IBM MessageSight via http plug-in using 
 16 	 * a {@link MessagingREST.Client} object. 
 17 	 * 
 18 	 * The methods are implemented as asynchronous JavaScript methods (even though the 
 19 	 * underlying protocol exchange might be synchronous in nature). This means they signal 
 20 	 * their completion by calling back to the application via Success or Failure callback 
 21 	 * functions provided by the application on the method in question. These callbacks are 
 22 	 * called at most once per method invocation and do not persist beyond the lifetime of 
 23 	 * the script that made the invocation.
 24 	 * 
 25 	 * @name MessagingREST.Client    
 26 	 * 
 27 	 * @constructor
 28 	 * 
 29 	 * @param {string} host - the address of the IBM MessageSight host, as a DNS name or dotted decimal IP address.
 30 	 * @param {number} port - the port number to connect to
 31 	 * @param {string} path - the alias on the IBM MessageSight host.
 32 	 * @param {string} clientid - the MessagingREST client identifier
 33 	 * @param {string} username - the username to authenticate with
 34 	 * @param {string} password - the password for the username provided
 35 	 *   
 36 	 */
 37 	var Client = function (host, port, path, protocol, clientid, username, password) {
 38 		
 39 		var trace = function() {};
 40 		
 41 		
 42 		if (typeof host !== "string")
 43 			throw new Error("invalid server name");
 44 		
 45 		if (typeof port !== "number" || port < 0)
 46 			throw new Error("invalid port number");
 47 			
 48 		if (typeof path !== "string")
 49 			throw new Error("invalid path");
 50 		
 51 		if (clientid && typeof clientid !== "string")
 52 			throw new Error("The ClientID is not valid");
 53 
 54 		var ipv6AddSBracket = (host.indexOf(":") != -1 && host.slice(0,1) != "[" && host.slice(-1) != "]");
 55 		var url = protocol + "://"+(ipv6AddSBracket?"["+host+"]":host)+":"+port+path;
 56 		
 57 		trace("Base URL is: " + url);
 58 		
 59 		
 60 		/**
 61 		 * Set a trace function that the client can use for
 62 		 * logging trace messages.
 63 		 * 
 64 		 * @param {function} trace - a trace function that takes one string as an argument
 65 		 */
 66 		this.setTrace = function(traceFunction) {
 67 			if (traceFunction) {
 68 				trace = traceFunction;
 69 			}
 70 		}
 71 
 72 		/** 
 73 		 * Set the necessary request headers for the a provided
 74 		 * XMLHttpRequest object. Since the request will more than
 75 		 * likely be a CORS request only headers that IBM MessageSight
 76 		 * allows may be set. 
 77 		 * @private
 78 		 * 
 79 		 * @name MessagingREST.Client#setRequestHeaders
 80 		 * @function
 81 		 * @param {object} request - The XMLHttpRequest object that headers should be set for.
 82 		 * 
 83 		 */
 84 		this.setRequestHeaders = function(request) {
 85 			// trace("Enter: Client.setRequestHeaders");
 86 					  
 87 			if (!request) {
 88 				return;
 89 			}
 90 			
 91 			if (username && password) {
 92 		    	var userPass = btoa(username + ":" + password); 
 93 		     	request.setRequestHeader("Authorization", "Basic " + userPass);
 94 			}
 95 			
 96 			if (clientid) {
 97 			    trace("setting header client-id to " + clientid);
 98 			    request.setRequestHeader("ClientID", clientid);
 99 			}    
100 		}
101 		
102 		/** 
103 		 * Invoke action to obtain retained messages from topic. 
104 		 * This API will invoke an asynchronous call on the IBM 
105 		 * MessageSight server that will attempt to obtain a retained
106 		 * message. After successful callback from this API a check
107 		 * for the retained message can be done with a call to 
108 		 * MessagingREST.Client#retrieveMessages
109 		 * 
110 		 * 
111 		 * @name MessagingREST.Client#getRetainedMsg
112 		 * @function
113 		 * @param {string} topicName - The topic name to obtain a retained message from.
114 		 * @param {function} onSuccess - Called after a response with a status code of 200 is received
115 		 *                              from the server. A single response parameter is passed 
116 		 *                              to the onSuccess callback:
117 		 *                              <ol>     
118 		 *                              <li>a string that contains the topic 
119 		 *                              <li>a string that contains any response from the request   
120 		 *                              </ol>
121 		 * @param {function} onFailure - Called when a status code other than 200 is received from
122 		 *                               the server. Two response parameters are passed to the 
123 		 *                               onFailure callback:
124 		 *                              <ol> 
125 		 *								<li>a number that indicates the response code from the server    
126 		 *                              <li>a string that contains any response text from the server
127 		 *                              </ol>
128 		 * 
129 		 */
130 		this.getRetainedMsg = function(topicName, onSuccess, onFailure) {
131 			// trace("Enter: Client.getRetainedMsg");
132 		    // GET /restmsg/topic/topicName
133 			var topicNameEncoded = encodeURIComponent(topicName);
134 			var requestUrl = url + "/message/" + topicNameEncoded;
135 		    var xhrRetainedMsgs = new XMLHttpRequest();
136 		    
137 		    xhrRetainedMsgs.open('GET', requestUrl, true);
138 		    trace("Request is GET " + requestUrl);
139 		    this.setRequestHeaders(xhrRetainedMsgs);
140 		    
141 		    xhrRetainedMsgs.onreadystatechange = function() {
142 		    	if (this.readyState != 4)  { return; }
143 		    	if (this.status === 200) {
144 		    		if (onSuccess) {
145                         var topic = this.getResponseHeader("Topic");
146                         onSuccess(topic, this.responseText);
147 		    		}
148 		    	} else {
149 		    		if (onFailure) {
150 		    			onFailure(this.status, this.statusText);
151 		    		}
152 		    	}
153 		    	this.onreadystatechange = function() { };
154 		    };
155 		    xhrRetainedMsgs.send();
156 		}
157 		
158 		/** 
159 		 * Invoke action to delete retained message from a
160 		 * given topic. 
161 		 * 
162 		 * 
163 		 * @name MessagingREST.Client#deleteRetained
164 		 * @function
165 		 * @param {string} topicName - The topic that the retained message should be removed from.
166 		 * @param {function} onSuccess - Called after a response with a status code of 200 is received
167 		 *                              from the server. A single response parameter is passed 
168 		 *                              to the onSuccess callback:
169 		 *                              <ol>     
170 		 *                              <li>a string that contains any response from the request   
171 		 *                              </ol>
172 		 * @param {function} onFailure - Called when a status code other than 200 is received from
173 		 *                               the server. Two response parameters are passed to the 
174 		 *                               onFailure callback:
175 		 *                              <ol> 
176 		 *								<li>a number that indicates the response code from the server    
177 		 *                              <li>a string that contains any response text from the server
178 		 *                              </ol>
179 		 * 
180 		 */
181 		this.deleteRetained = function(topicName, onSuccess, onFailure) {
182 			// trace("Enter: Client.deleteRetained");
183 				
184 		    // DELETE /restmsg/topic/topicName
185 			var topicNameEncoded = encodeURIComponent(topicName);
186 			var requestUrl = url + "/message/" + topicNameEncoded;
187 		    var xhrDeleteRetained = new XMLHttpRequest();
188 		    
189 		    trace("Request is DELETE " + requestUrl);
190 		    xhrDeleteRetained.open('DELETE', requestUrl, true);
191 		    this.setRequestHeaders(xhrDeleteRetained);
192 		    
193 		    xhrDeleteRetained.onreadystatechange = function() {
194 		    	if (this.readyState != 4)  { return; }
195 		    	if (this.status === 200) {
196 		    		if (onSuccess) {
197 		    			onSuccess(this.responseText);
198 		    		}
199 		    	} else {
200 		    		if (onFailure) {
201 		    			onFailure(this.status, this.statusText);
202 		    		}
203 		    	}
204 		    	this.onreadystatechange = function() { };
205 		    };
206 		    xhrDeleteRetained.send(null);
207 		}
208 
209 		/** 
210 		 * Publish a message to a specific topic.
211 		 * 
212 		 * 
213 		 * @name MessagingREST.Client#createTopicSubscription
214 		 * @function
215 		 * @param {string} topicName - the topic filter that the subscription should be set to
216 		 * @param {boolean} persist - if the message should persist
217 		 * @param {boolean} retain - if the message should be retained
218 		 * @param {function} onSuccess - Called after a response with a status code of 200 is received
219 		 *                              from the server. A single response parameter is passed 
220 		 *                              to the onSuccess callback:
221 		 *                              <ol>     
222 		 *                              <li>a string that contains any response from the request   
223 		 *                              </ol>
224 		 * @param {function} onFailure - Called when a status code other than 200 is received from
225 		 *                               the server. Two response parameters are passed to the 
226 		 *                               onFailure callback:
227 		 *                              <ol> 
228 		 *								<li>a number that indicates the response code from the server    
229 		 *                              <li>a string that contains any response text from the server
230 		 *                              </ol>
231 		 */
232 		this.publishMessageTopic = function(topicName, persist, retain, onSuccess, onFailure) {
233 			// trace("Enter: Client.publishMessageTopic");
234 
235 		    // POST /restmsg/topic/topicname?persist=bool&retain=bool
236 		    var topicNameEncoded = encodeURIComponent(topicName);
237 			var requestUrl;
238 		    var xhrPublish = new XMLHttpRequest();
239 		    if (retain) {
240 		        requestUrl = url + "/message/" + topicNameEncoded;
241 		        xhrPublish.open('PUT', requestUrl, true);
242 		        trace("Request is PUT " + requestUrl);
243 		    } else {
244 		        requestUrl = url + "/message/" + topicNameEncoded + "?persist=" + persist.toString();
245 		        xhrPublish.open('POST', requestUrl, true);
246 		        trace("Request is POST " + requestUrl);
247 		    }    
248 
249 		    this.setRequestHeaders(xhrPublish);
250 		    xhrPublish.setRequestHeader("Content-Type", "text/plain");
251 
252 		    xhrPublish.onreadystatechange = function() {
253 		    	if (this.readyState != 4)  { return; }
254 		    	if (this.status === 200) {
255 		    		if (onSuccess) {
256 		    			onSuccess(this.responseText);
257 		    		}
258 		    	} else {
259 		    		if (onFailure) {
260 		    			onFailure(this.status, this.responseText);
261 		    		}
262 		    	}
263 		    	this.onreadystatechange = function() { };
264 		    };
265 		    
266 		    var msgObj = {message: topicForm.textMessage.value }
267 		    var msgString = JSON.stringify(msgObj);
268 		    xhrPublish.send(topicForm.textMessage.value);
269 		}
270 	};
271 
272 
273 	// Module contents.
274 	return {
275 		Client: Client
276 	};
277 
278 })(window);
279