PageRenderTime 36ms CodeModel.GetById 19ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Service/WindowsAzure/Storage/Queue.php

http://rewardvn.googlecode.com/
PHP | 555 lines | 306 code | 57 blank | 192 comment | 85 complexity | e6f7ff9a8feab388d3a30235e709488e MD5 | raw file
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Service_WindowsAzure
 17 * @subpackage Storage
 18 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 19 * @license    http://todo     name_todo
 20 * @version    $Id$
 21 */
 22
 23/**
 24 * @see Zend_Service_WindowsAzure_Credentials_SharedKey
 25 */
 26require_once 'Zend/Service/WindowsAzure/Credentials/SharedKey.php';
 27
 28/**
 29 * @see Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
 30 */
 31require_once 'Zend/Service/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php';
 32
 33/**
 34 * @see Zend_Http_Client
 35 */
 36require_once 'Zend/Http/Client.php';
 37
 38/**
 39 * @see Zend_Http_Response
 40 */
 41require_once 'Zend/Http/Response.php';
 42
 43/**
 44 * @see Zend_Service_WindowsAzure_Storage
 45 */
 46require_once 'Zend/Service/WindowsAzure/Storage.php';
 47
 48/**
 49 * Zend_Service_WindowsAzure_Storage_QueueInstance
 50 */
 51require_once 'Zend/Service/WindowsAzure/Storage/QueueInstance.php';
 52
 53/**
 54 * Zend_Service_WindowsAzure_Storage_QueueMessage
 55 */
 56require_once 'Zend/Service/WindowsAzure/Storage/QueueMessage.php';
 57
 58/**
 59 * @see Zend_Service_WindowsAzure_Exception
 60 */
 61require_once 'Zend/Service/WindowsAzure/Exception.php';
 62
 63
 64/**
 65 * @category   Zend
 66 * @package    Zend_Service_WindowsAzure
 67 * @subpackage Storage
 68 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 69 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 70 */
 71class Zend_Service_WindowsAzure_Storage_Queue extends Zend_Service_WindowsAzure_Storage
 72{
 73	/**
 74	 * Maximal message size (in bytes)
 75	 */
 76	const MAX_MESSAGE_SIZE = 8388608;
 77	
 78	/**
 79	 * Maximal message ttl (in seconds)
 80	 */
 81	const MAX_MESSAGE_TTL = 604800;
 82	
 83	/**
 84	 * Creates a new Zend_Service_WindowsAzure_Storage_Queue instance
 85	 *
 86	 * @param string $host Storage host name
 87	 * @param string $accountName Account name for Windows Azure
 88	 * @param string $accountKey Account key for Windows Azure
 89	 * @param boolean $usePathStyleUri Use path-style URI's
 90	 * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
 91	 */
 92	public function __construct($host = Zend_Service_WindowsAzure_Storage::URL_DEV_QUEUE, $accountName = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_ACCOUNT, $accountKey = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_KEY, $usePathStyleUri = false, Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null)
 93	{
 94		parent::__construct($host, $accountName, $accountKey, $usePathStyleUri, $retryPolicy);
 95		
 96		// API version
 97		$this->_apiVersion = '2009-09-19';
 98	}
 99	
100	/**
101	 * Check if a queue exists
102	 * 
103	 * @param string $queueName Queue name
104	 * @return boolean
105	 */
106	public function queueExists($queueName = '')
107	{
108		if ($queueName === '') {
109			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
110		}
111		if (!self::isValidQueueName($queueName)) {
112		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
113		}
114			
115		// List queues
116        $queues = $this->listQueues($queueName, 1);
117        foreach ($queues as $queue) {
118            if ($queue->Name == $queueName) {
119                return true;
120            }
121        }
122        
123        return false;
124	}
125	
126	/**
127	 * Create queue
128	 *
129	 * @param string $queueName Queue name
130	 * @param array  $metadata  Key/value pairs of meta data
131	 * @return object Queue properties
132	 * @throws Zend_Service_WindowsAzure_Exception
133	 */
134	public function createQueue($queueName = '', $metadata = array())
135	{
136		if ($queueName === '') {
137			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
138		}
139		if (!self::isValidQueueName($queueName)) {
140		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
141		}
142			
143		// Create metadata headers
144		$headers = array();
145		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); 
146		
147		// Perform request
148		$response = $this->_performRequest($queueName, '', Zend_Http_Client::PUT, $headers);	
149		if ($response->isSuccessful()) {
150		    return new Zend_Service_WindowsAzure_Storage_QueueInstance(
151		        $queueName,
152		        $metadata
153		    );
154		} else {
155			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
156		}
157	}
158	
159	/**
160	 * Get queue
161	 * 
162	 * @param string $queueName  Queue name
163	 * @return Zend_Service_WindowsAzure_Storage_QueueInstance
164	 * @throws Zend_Service_WindowsAzure_Exception
165	 */
166	public function getQueue($queueName = '')
167	{
168		if ($queueName === '') {
169			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
170		}
171		if (!self::isValidQueueName($queueName)) {
172		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
173		}
174		    
175		// Perform request
176		$response = $this->_performRequest($queueName, '?comp=metadata', Zend_Http_Client::GET);	
177		if ($response->isSuccessful()) {
178		    // Parse metadata
179		    $metadata = $this->_parseMetadataHeaders($response->getHeaders());
180
181		    // Return queue
182		    $queue = new Zend_Service_WindowsAzure_Storage_QueueInstance(
183		        $queueName,
184		        $metadata
185		    );
186		    $queue->ApproximateMessageCount = intval($response->getHeader('x-ms-approximate-message-count'));
187		    return $queue;
188		} else {
189		    throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
190		}
191	}
192	
193	/**
194	 * Get queue metadata
195	 * 
196	 * @param string $queueName  Queue name
197	 * @return array Key/value pairs of meta data
198	 * @throws Zend_Service_WindowsAzure_Exception
199	 */
200	public function getQueueMetadata($queueName = '')
201	{
202		if ($queueName === '') {
203			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
204		}
205		if (!self::isValidQueueName($queueName)) {
206		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
207		}
208			
209	    return $this->getQueue($queueName)->Metadata;
210	}
211	
212	/**
213	 * Set queue metadata
214	 * 
215	 * Calling the Set Queue Metadata operation overwrites all existing metadata that is associated with the queue. It's not possible to modify an individual name/value pair.
216	 *
217	 * @param string $queueName  Queue name
218	 * @param array  $metadata       Key/value pairs of meta data
219	 * @throws Zend_Service_WindowsAzure_Exception
220	 */
221	public function setQueueMetadata($queueName = '', $metadata = array())
222	{
223		if ($queueName === '') {
224			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
225		}
226		if (!self::isValidQueueName($queueName)) {
227		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
228		}
229		if (count($metadata) == 0) {
230		    return;
231		}
232		    
233		// Create metadata headers
234		$headers = array();
235		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata)); 
236		
237		// Perform request
238		$response = $this->_performRequest($queueName, '?comp=metadata', Zend_Http_Client::PUT, $headers);
239
240		if (!$response->isSuccessful()) {
241			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
242		}
243	}
244	
245	/**
246	 * Delete queue
247	 *
248	 * @param string $queueName Queue name
249	 * @throws Zend_Service_WindowsAzure_Exception
250	 */
251	public function deleteQueue($queueName = '')
252	{
253		if ($queueName === '') {
254			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
255		}
256		if (!self::isValidQueueName($queueName)) {
257		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
258		}
259			
260		// Perform request
261		$response = $this->_performRequest($queueName, '', Zend_Http_Client::DELETE);
262		if (!$response->isSuccessful()) {
263			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
264		}
265	}
266	
267	/**
268	 * List queues
269	 *
270	 * @param string $prefix     Optional. Filters the results to return only queues whose name begins with the specified prefix.
271	 * @param int    $maxResults Optional. Specifies the maximum number of queues to return per call to Azure storage. This does NOT affect list size returned by this function. (maximum: 5000)
272	 * @param string $marker     Optional string value that identifies the portion of the list to be returned with the next list operation.
273	 * @param string $include    Optional. Include this parameter to specify that the queue's metadata be returned as part of the response body. (allowed values: '', 'metadata')
274	 * @param int    $currentResultCount Current result count (internal use)
275	 * @return array
276	 * @throws Zend_Service_WindowsAzure_Exception
277	 */
278	public function listQueues($prefix = null, $maxResults = null, $marker = null, $include = null, $currentResultCount = 0)
279	{
280	    // Build query string
281		$queryString = array('comp=list');
282        if (!is_null($prefix)) {
283	        $queryString[] = 'prefix=' . $prefix;
284        }
285	    if (!is_null($maxResults)) {
286	        $queryString[] = 'maxresults=' . $maxResults;
287	    }
288	    if (!is_null($marker)) {
289	        $queryString[] = 'marker=' . $marker;
290	    }
291		if (!is_null($include)) {
292	        $queryString[] = 'include=' . $include;
293	    }
294	    $queryString = self::createQueryStringFromArray($queryString);
295	        
296		// Perform request
297		$response = $this->_performRequest('', $queryString, Zend_Http_Client::GET);	
298		if ($response->isSuccessful()) {
299			$xmlQueues = $this->_parseResponse($response)->Queues->Queue;
300			$xmlMarker = (string)$this->_parseResponse($response)->NextMarker;
301
302			$queues = array();
303			if (!is_null($xmlQueues)) {
304				for ($i = 0; $i < count($xmlQueues); $i++) {
305					$queues[] = new Zend_Service_WindowsAzure_Storage_QueueInstance(
306						(string)$xmlQueues[$i]->Name,
307						$this->_parseMetadataElement($xmlQueues[$i])
308					);
309				}
310			}
311			$currentResultCount = $currentResultCount + count($queues);
312			if (!is_null($maxResults) && $currentResultCount < $maxResults) {
313    			if (!is_null($xmlMarker) && $xmlMarker != '') {
314    			    $queues = array_merge($queues, $this->listQueues($prefix, $maxResults, $xmlMarker, $include, $currentResultCount));
315    			}
316			}
317			if (!is_null($maxResults) && count($queues) > $maxResults) {
318			    $queues = array_slice($queues, 0, $maxResults);
319			}
320			    
321			return $queues;
322		} else {
323			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
324		}
325	}
326	
327	/**
328	 * Put message into queue
329	 *
330	 * @param string $queueName  Queue name
331	 * @param string $message    Message
332	 * @param int    $ttl        Message Time-To-Live (in seconds). Defaults to 7 days if the parameter is omitted.
333	 * @throws Zend_Service_WindowsAzure_Exception
334	 */
335	public function putMessage($queueName = '', $message = '', $ttl = null)
336	{
337		if ($queueName === '') {
338			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
339		}
340		if (!self::isValidQueueName($queueName)) {
341		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
342		}
343		if (strlen($message) > self::MAX_MESSAGE_SIZE) {
344		    throw new Zend_Service_WindowsAzure_Exception('Message is too big. Message content should be < 8KB.');
345		}
346		if ($message == '') {
347		    throw new Zend_Service_WindowsAzure_Exception('Message is not specified.');
348		}
349		if (!is_null($ttl) && ($ttl <= 0 || $ttl > self::MAX_MESSAGE_SIZE)) {
350		    throw new Zend_Service_WindowsAzure_Exception('Message TTL is invalid. Maximal TTL is 7 days (' . self::MAX_MESSAGE_SIZE . ' seconds) and should be greater than zero.');
351		}
352		    
353	    // Build query string
354		$queryString = array();
355        if (!is_null($ttl)) {
356	        $queryString[] = 'messagettl=' . $ttl;
357        }
358	    $queryString = self::createQueryStringFromArray($queryString);
359	        
360	    // Build body
361	    $rawData = '';
362	    $rawData .= '<QueueMessage>';
363	    $rawData .= '    <MessageText>' . base64_encode($message) . '</MessageText>';
364	    $rawData .= '</QueueMessage>';
365	        
366		// Perform request
367		$response = $this->_performRequest($queueName . '/messages', $queryString, Zend_Http_Client::POST, array(), false, $rawData);
368
369		if (!$response->isSuccessful()) {
370			throw new Zend_Service_WindowsAzure_Exception('Error putting message into queue.');
371		}
372	}
373	
374	/**
375	 * Get queue messages
376	 *
377	 * @param string $queueName         Queue name
378	 * @param string $numOfMessages     Optional. A nonzero integer value that specifies the number of messages to retrieve from the queue, up to a maximum of 32. By default, a single message is retrieved from the queue with this operation.
379	 * @param int    $visibilityTimeout Optional. An integer value that specifies the message's visibility timeout in seconds. The maximum value is 2 hours. The default message visibility timeout is 30 seconds.
380	 * @param string $peek              Peek only?
381	 * @return array
382	 * @throws Zend_Service_WindowsAzure_Exception
383	 */
384	public function getMessages($queueName = '', $numOfMessages = 1, $visibilityTimeout = null, $peek = false)
385	{
386		if ($queueName === '') {
387			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
388		}
389		if (!self::isValidQueueName($queueName)) {
390		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
391		}
392		if ($numOfMessages < 1 || $numOfMessages > 32 || intval($numOfMessages) != $numOfMessages) {
393		    throw new Zend_Service_WindowsAzure_Exception('Invalid number of messages to retrieve.');
394		}
395		if (!is_null($visibilityTimeout) && ($visibilityTimeout <= 0 || $visibilityTimeout > 7200)) {
396		    throw new Zend_Service_WindowsAzure_Exception('Visibility timeout is invalid. Maximum value is 2 hours (7200 seconds) and should be greater than zero.');
397		}
398		    
399	    // Build query string
400		$queryString = array();
401    	if ($peek) {
402    	    $queryString[] = 'peekonly=true';
403    	}
404    	if ($numOfMessages > 1) {
405	        $queryString[] = 'numofmessages=' . $numOfMessages;
406    	}
407    	if (!$peek && !is_null($visibilityTimeout)) {
408	        $queryString[] = 'visibilitytimeout=' . $visibilityTimeout;
409    	}   
410	    $queryString = self::createQueryStringFromArray($queryString);
411	        
412		// Perform request
413		$response = $this->_performRequest($queueName . '/messages', $queryString, Zend_Http_Client::GET);	
414		if ($response->isSuccessful()) {
415		    // Parse results
416			$result = $this->_parseResponse($response);
417		    if (!$result) {
418		        return array();
419		    }
420
421		    $xmlMessages = null;
422		    if (count($result->QueueMessage) > 1) {
423    		    $xmlMessages = $result->QueueMessage;
424    		} else {
425    		    $xmlMessages = array($result->QueueMessage);
426    		}
427
428			$messages = array();
429			for ($i = 0; $i < count($xmlMessages); $i++) {
430				$messages[] = new Zend_Service_WindowsAzure_Storage_QueueMessage(
431					(string)$xmlMessages[$i]->MessageId,
432					(string)$xmlMessages[$i]->InsertionTime,
433					(string)$xmlMessages[$i]->ExpirationTime,
434					($peek ? '' : (string)$xmlMessages[$i]->PopReceipt),
435					($peek ? '' : (string)$xmlMessages[$i]->TimeNextVisible),
436					(string)$xmlMessages[$i]->DequeueCount,
437					base64_decode((string)$xmlMessages[$i]->MessageText)
438			    );
439			}
440			    
441			return $messages;
442		} else {
443			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
444		}
445	}
446	
447	/**
448	 * Peek queue messages
449	 *
450	 * @param string $queueName         Queue name
451	 * @param string $numOfMessages     Optional. A nonzero integer value that specifies the number of messages to retrieve from the queue, up to a maximum of 32. By default, a single message is retrieved from the queue with this operation.
452	 * @return array
453	 * @throws Zend_Service_WindowsAzure_Exception
454	 */
455	public function peekMessages($queueName = '', $numOfMessages = 1)
456	{
457	    return $this->getMessages($queueName, $numOfMessages, null, true);
458	}
459	
460	/**
461	 * Clear queue messages
462	 *
463	 * @param string $queueName         Queue name
464	 * @throws Zend_Service_WindowsAzure_Exception
465	 */
466	public function clearMessages($queueName = '')
467	{
468		if ($queueName === '') {
469			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
470		}
471		if (!self::isValidQueueName($queueName)) {
472		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
473		}
474
475		// Perform request
476		$response = $this->_performRequest($queueName . '/messages', '', Zend_Http_Client::DELETE);	
477		if (!$response->isSuccessful()) {
478			throw new Zend_Service_WindowsAzure_Exception('Error clearing messages from queue.');
479		}
480	}
481	
482	/**
483	 * Delete queue message
484	 *
485	 * @param string $queueName         					Queue name
486	 * @param Zend_Service_WindowsAzure_Storage_QueueMessage $message Message to delete from queue. A message retrieved using "peekMessages" can NOT be deleted!
487	 * @throws Zend_Service_WindowsAzure_Exception
488	 */
489	public function deleteMessage($queueName = '', Zend_Service_WindowsAzure_Storage_QueueMessage $message)
490	{
491		if ($queueName === '') {
492			throw new Zend_Service_WindowsAzure_Exception('Queue name is not specified.');
493		}
494		if (!self::isValidQueueName($queueName)) {
495		    throw new Zend_Service_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
496		}
497		if ($message->PopReceipt == '') {
498		    throw new Zend_Service_WindowsAzure_Exception('A message retrieved using "peekMessages" can NOT be deleted! Use "getMessages" instead.');
499		}
500
501		// Perform request
502		$response = $this->_performRequest($queueName . '/messages/' . $message->MessageId, '?popreceipt=' . $message->PopReceipt, Zend_Http_Client::DELETE);	
503		if (!$response->isSuccessful()) {
504			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
505		}
506	}
507	
508	/**
509	 * Is valid queue name?
510	 *
511	 * @param string $queueName Queue name
512	 * @return boolean
513	 */
514    public static function isValidQueueName($queueName = '')
515    {
516        if (preg_match("/^[a-z0-9][a-z0-9-]*$/", $queueName) === 0) {
517            return false;
518        }
519    
520        if (strpos($queueName, '--') !== false) {
521            return false;
522        }
523    
524        if (strtolower($queueName) != $queueName) {
525            return false;
526        }
527    
528        if (strlen($queueName) < 3 || strlen($queueName) > 63) {
529            return false;
530        }
531            
532        if (substr($queueName, -1) == '-') {
533            return false;
534        }
535    
536        return true;
537    }
538    
539	/**
540	 * Get error message from Zend_Http_Response
541	 * 
542	 * @param Zend_Http_Response $response Repsonse
543	 * @param string $alternativeError Alternative error message
544	 * @return string
545	 */
546	protected function _getErrorMessage(Zend_Http_Response $response, $alternativeError = 'Unknown error.')
547	{
548		$response = $this->_parseResponse($response);
549		if ($response && $response->Message) {
550		    return (string)$response->Message;
551		} else {
552		    return $alternativeError;
553		}
554	}
555}