PageRenderTime 61ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/org/codehaus/stomp/Stomp.as

http://as3-stomp.googlecode.com/
ActionScript | 489 lines | 375 code | 90 blank | 24 comment | 70 complexity | 21a8e08d16f5394019d29bca731c80c1 MD5 | raw file
  1. /**
  2. *
  3. * Licensed to the Apache Software Foundation (ASF) under one or more
  4. * contributor license agreements. See the NOTICE file distributed with
  5. * this work for additional information regarding copyright ownership.
  6. * The ASF licenses this file to You under the Apache License, Version 2.0
  7. * (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. /*
  19. Version 0.1 : R Jewson (rjewson at gmail dot com). First release, only for reciept of messages.
  20. Version 0.7 : Derek Wischusen (dwischus at flexonrails dot net), Peter Mulreid, and mark81.
  21. */
  22. package org.codehaus.stomp {
  23. import flash.errors.IOError;
  24. import flash.events.*;
  25. import flash.net.Socket;
  26. import flash.utils.ByteArray;
  27. import flash.utils.Timer;
  28. import org.codehaus.stomp.event.*;
  29. import org.codehaus.stomp.frame.*;
  30. import org.codehaus.stomp.headers.*;
  31. import org.rxr.utils.ByteArrayReader;
  32. [Event(name="connected", type="org.codehaus.stomp.event.ConnectedEvent")]
  33. [Event(name="message", type="org.codehaus.stomp.event.MessageEvent")]
  34. [Event(name="receipt", type="org.codehaus.stomp.event.ReceiptEvent")]
  35. [Event(name="fault", type="org.codehaus.stomp.event.STOMPErrorEvent")]
  36. [Event(name="reconnectFailed", type="org.codehaus.stomp.event.ReconnectFailedEvent")]
  37. [Event(name="ioError", type="flash.events.IOErrorEvent")]
  38. [Event(name="securityError", type="flash.events.SecurityErrorEvent")]
  39. public class Stomp extends EventDispatcher {
  40. private static const NEWLINE : String = "\n";
  41. private static const BODY_START : String = "\n\n";
  42. private static const NULL_BYTE : int = 0x00;
  43. private var socket : Socket;
  44. private var buffer:ByteArrayReader = new ByteArrayReader();
  45. private var server : String;
  46. private var port : int;
  47. private var connectHeaders : ConnectHeaders;
  48. private var connectTimer : Timer;
  49. private var subscriptions : Array = new Array();
  50. public var errorMessages : Array = new Array();
  51. public var sessionID : String;
  52. public var connectTime : Date;
  53. public var disconnectTime : Date;
  54. public var autoReconnect : Boolean = true;
  55. public function Stomp()
  56. {
  57. }
  58. public function connect( server : String = "localhost", port : int = 61613, connectHeaders : ConnectHeaders = null, socket: Socket = null) : void
  59. {
  60. this.server = server;
  61. this.port = port
  62. this.connectHeaders = connectHeaders;
  63. this.socket = socket || new Socket();
  64. initializeSocket();
  65. doConnect();
  66. }
  67. public function close() : void
  68. {
  69. if (connectTimer && connectTimer.running)
  70. stopAutoReconnect();
  71. try {
  72. if (socket.connected)
  73. disconnect();
  74. socket.close();
  75. } catch (e:Error) {
  76. trace('Non-critical error closing socket ', e.toString());
  77. }
  78. }
  79. public function stopAutoReconnect(): void
  80. {
  81. if (connectTimer)
  82. {
  83. connectTimer.stop();
  84. connectTimer.removeEventListener(TimerEvent.TIMER, doConnectTimer);
  85. connectTimer = null;
  86. }
  87. }
  88. private function initializeSocket(): void
  89. {
  90. socket.addEventListener( Event.CONNECT, onConnect );
  91. socket.addEventListener( Event.CLOSE, onClose );
  92. socket.addEventListener( ProgressEvent.SOCKET_DATA, onData );
  93. socket.addEventListener( IOErrorEvent.IO_ERROR, onError );
  94. socket.addEventListener( SecurityErrorEvent.SECURITY_ERROR, onError);
  95. }
  96. private function doConnect() : void
  97. {
  98. if (!socket.connected)
  99. socket.connect( server, int(port) );
  100. }
  101. protected function onConnect( event:Event ) : void
  102. {
  103. if (connectTimer && connectTimer.running)
  104. stopAutoReconnect();
  105. var h : Object = connectHeaders ? connectHeaders.getHeaders() : {};
  106. transmit("CONNECT", h);
  107. dispatchEvent(event.clone());
  108. }
  109. protected function tryAutoreconnect() : void
  110. {
  111. if (!socket.connected && autoReconnect && (connectTimer == null || !connectTimer.running))
  112. {
  113. // try every minute, repeating indefinitely (want it to be longer than socket timeouts)
  114. connectTimer = new Timer(60000, 0);
  115. connectTimer.addEventListener(TimerEvent.TIMER, doConnectTimer);
  116. connectTimer.start();
  117. }
  118. }
  119. // these are always unexpected close events (they don't result from us calling socket.close() (see docs))
  120. protected function onClose( event:Event ) : void
  121. {
  122. disconnectTime = new Date();
  123. tryAutoreconnect();
  124. dispatchEvent(event.clone());
  125. }
  126. private function doConnectTimer( event:TimerEvent ):void
  127. {
  128. if (!socket.connected)
  129. {
  130. doConnect();
  131. }
  132. }
  133. private function onError( event:Event ) : void
  134. {
  135. var now: Date = new Date();
  136. try {
  137. socket.close();
  138. } catch (io:IOError) {
  139. trace('IOError', io.toString());
  140. }
  141. if (connectTimer != null && connectTimer.running)
  142. {
  143. dispatchEvent(new ReconnectFailedEvent(ReconnectFailedEvent.RECONNECT_FAILED));
  144. }
  145. else
  146. {
  147. disconnectTime = now;
  148. tryAutoreconnect();
  149. errorMessages.push(now + " " + event.type);
  150. dispatchEvent(event.clone());
  151. }
  152. }
  153. public function subscribe(destination : String, headers : SubscribeHeaders = null) : void
  154. {
  155. var h : Object = headers ? headers.getHeaders() : null;
  156. if (socket.connected)
  157. {
  158. if (!h) h = {};
  159. h['destination'] = destination;
  160. transmit("SUBSCRIBE", h);
  161. }
  162. subscriptions.push({destination: destination, headers: headers, connected: socket.connected});
  163. }
  164. public function send (destination : String, message : Object, headers : SendHeaders = null) : void
  165. {
  166. var h : Object = headers ? headers.getHeaders() : {};
  167. h['destination'] = destination;
  168. var messageBytes : ByteArray = new ByteArray();
  169. if(message is ByteArray)
  170. messageBytes.writeBytes(ByteArray(message), 0, message.length);
  171. else if(message is String)
  172. messageBytes.writeUTFBytes(String(message));
  173. else if(message is int)
  174. messageBytes.writeInt(int(message));
  175. else if(message is Number)
  176. messageBytes.writeDouble(Number(message));
  177. else if(message is Boolean)
  178. messageBytes.writeBoolean(Boolean(message));
  179. else
  180. messageBytes.writeObject(message);
  181. h['content-length'] = messageBytes.length;
  182. transmit("SEND", h, messageBytes);
  183. }
  184. public function sendTextMessage(destination : String, message : String, headers : SendHeaders = null) : void
  185. {
  186. var h : Object = headers ? headers.getHeaders() : {};
  187. h['destination'] = destination;
  188. var messageBytes : ByteArray = new ByteArray();
  189. messageBytes.writeUTFBytes(message);
  190. transmit("SEND", h, messageBytes);
  191. }
  192. public function begin (transaction : String, headers : BeginHeaders = null) : void
  193. {
  194. var h : Object = headers ? headers.getHeaders() : {};
  195. h['transaction'] = transaction;
  196. transmit("BEGIN", h);
  197. }
  198. public function commit (transaction : String, headers : CommitHeaders = null) : void
  199. {
  200. var h : Object = headers ? headers.getHeaders() : {};
  201. h['transaction'] = transaction;
  202. transmit("COMMIT", h);
  203. }
  204. public function ack (messageID : String, headers : AckHeaders = null) : void
  205. {
  206. var h : Object = headers ? headers.getHeaders() : {};
  207. h['message-id'] = messageID;
  208. transmit("ACK", h);
  209. }
  210. public function abort (transaction : String, headers : AbortHeaders = null) : void
  211. {
  212. var h : Object = headers ? headers.getHeaders() : {};
  213. h['transaction'] = transaction;
  214. transmit("ABORT", h);
  215. }
  216. public function unsubscribe (destination : String, headers : UnsubscribeHeaders = null) : void
  217. {
  218. var h : Object = headers ? headers.getHeaders() : {};
  219. h['destination'] = destination;
  220. transmit("UNSUBSCRIBE", h);
  221. }
  222. public function disconnect () : void
  223. {
  224. transmit("DISCONNECT", {});
  225. }
  226. private function transmit (command : String, headers : Object, body : ByteArray = null) : void
  227. {
  228. var transmission : ByteArray = new ByteArray();
  229. transmission.writeUTFBytes(command);
  230. for (var header: String in headers)
  231. transmission.writeUTFBytes( NEWLINE + header + ":" + headers[header] );
  232. transmission.writeUTFBytes( BODY_START );
  233. if (body) transmission.writeBytes( body, 0, body.length )
  234. transmission.writeByte( NULL_BYTE );
  235. socket.writeBytes( transmission, 0, transmission.length );
  236. socket.flush();
  237. }
  238. private function processSubscriptions() : void
  239. {
  240. for each (var sub : Object in subscriptions)
  241. {
  242. if (sub['connected'] == false)
  243. this.subscribe(sub['destination'], SubscribeHeaders(sub['headers']));
  244. }
  245. }
  246. private var frameReader : FrameReader;
  247. private function onData(event : ProgressEvent) : void
  248. {
  249. if (buffer.bytesAvailable == 0)
  250. buffer.length = 0;
  251. socket.readBytes(buffer, buffer.length, socket.bytesAvailable);
  252. while (buffer.bytesAvailable > 0 && processFrame()) {
  253. // processFrame called once per iteration;
  254. }
  255. }
  256. private function processFrame(): Boolean
  257. {
  258. if (!frameReader)
  259. frameReader = new FrameReader(buffer);
  260. else
  261. frameReader.processBytes();
  262. if (frameReader.isComplete)
  263. {
  264. dispatchFrame(frameReader.command, frameReader.headers, frameReader.body);
  265. frameReader = null;
  266. return true;
  267. }
  268. else
  269. {
  270. return false;
  271. }
  272. }
  273. private function dispatchFrame(command: String, headers: Object, body: ByteArray): void
  274. {
  275. switch (command)
  276. {
  277. case "CONNECTED":
  278. connectTime = new Date();
  279. sessionID = headers['session'];
  280. processSubscriptions();
  281. dispatchEvent(new ConnectedEvent(ConnectedEvent.CONNECTED));
  282. break;
  283. case "MESSAGE":
  284. var messageEvent : MessageEvent = new MessageEvent(MessageEvent.MESSAGE);
  285. messageEvent.message = new MessageFrame(body, headers);
  286. dispatchEvent(messageEvent);
  287. break;
  288. case "RECEIPT":
  289. var receiptEvent : ReceiptEvent = new ReceiptEvent(ReceiptEvent.RECEIPT);
  290. receiptEvent.receiptID = headers['receipt-id'];
  291. dispatchEvent(receiptEvent);
  292. break;
  293. case "ERROR":
  294. var errorEvent : STOMPErrorEvent = new STOMPErrorEvent(STOMPErrorEvent.ERROR);
  295. errorEvent.error = new ErrorFrame(body, headers);
  296. dispatchEvent(errorEvent);
  297. break;
  298. default:
  299. throw new Error("UNKNOWN STOMP FRAME '"+command+"'");
  300. break;
  301. }
  302. }
  303. }
  304. }
  305. import org.rxr.utils.ByteArrayReader;
  306. import flash.utils.ByteArray;
  307. import flash.utils.IDataInput;
  308. import org.codehaus.stomp.Stomp;
  309. internal class FrameReader {
  310. private var reader : ByteArrayReader;
  311. private var frameComplete: Boolean = false;
  312. private var contentLength: int = -1;
  313. public var command : String;
  314. public var headers : Object;
  315. public var body : ByteArray = new ByteArray();
  316. private var bodyProcessed:Boolean = false;
  317. public function get isComplete(): Boolean
  318. {
  319. return frameComplete;
  320. }
  321. public function readBytes(data: IDataInput): void
  322. {
  323. data.readBytes(reader, reader.length, data.bytesAvailable);
  324. processBytes();
  325. }
  326. public function processBytes(): void
  327. {
  328. if (!command && reader.scan(0x0A) != -1)
  329. processCommand();
  330. if (command && !headers && reader.indexOfString("\n\n") != -1)
  331. processHeaders();
  332. if (command && headers && (bodyProcessed=bodyComplete()))
  333. processBody();
  334. if (command && headers && bodyProcessed)
  335. frameComplete = true;
  336. }
  337. private function processCommand(): void
  338. {
  339. command = reader.readLine();
  340. }
  341. private function processHeaders(): void
  342. {
  343. headers = new Object();
  344. var headerString : String = reader.readUntilString("\n\n");
  345. var headerValuePairs : Array = headerString.split("\n");
  346. for each (var pair : String in headerValuePairs)
  347. {
  348. var separator : int = pair.indexOf(":");
  349. headers[pair.substring(0, separator)] = pair.substring(separator+1);
  350. }
  351. if(headers["content-length"])
  352. contentLength = headers["content-length"];
  353. reader.forward();
  354. }
  355. private function processBody(): void
  356. {
  357. while (reader.bytesAvailable > 0 && reader.peek(0x00) <= 27) {
  358. reader.forward();
  359. }
  360. body.position=0;
  361. }
  362. private function bodyComplete() : Boolean
  363. {
  364. if(contentLength != -1)
  365. {
  366. const len: int = body.length;
  367. if(contentLength > reader.bytesAvailable + len)
  368. {
  369. body.writeBytes(reader.readFor(reader.bytesAvailable));
  370. return false;
  371. }
  372. else
  373. {
  374. body.writeBytes(reader.readFor(contentLength - len));
  375. }
  376. }
  377. else
  378. {
  379. var nullByteIndex: int = reader.scan(0x00);
  380. if(nullByteIndex != -1)
  381. {
  382. if (nullByteIndex > 0)
  383. body.writeBytes(reader.readFor(nullByteIndex));
  384. contentLength = body.length;
  385. }
  386. else
  387. {
  388. body.writeBytes(reader.readFor(reader.bytesAvailable));
  389. return false;
  390. }
  391. }
  392. return true;
  393. }
  394. public function FrameReader(reader: ByteArrayReader): void
  395. {
  396. this.reader = reader;
  397. processBytes();
  398. }
  399. }