PageRenderTime 23ms CodeModel.GetById 2ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/MoodleWebRole/azure/Microsoft/WindowsAzure/Storage/Queue.php

#
PHP | 552 lines | 291 code | 57 blank | 204 comment | 82 complexity | 8c6c6c4ed1da2cbdeefd47e0d5b35ddc MD5 | raw file
  1<?php
  2/**
  3 * Copyright (c) 2009, RealDolmen
  4 * All rights reserved.
  5 *
  6 * Redistribution and use in source and binary forms, with or without
  7 * modification, are permitted provided that the following conditions are met:
  8 *     * Redistributions of source code must retain the above copyright
  9 *       notice, this list of conditions and the following disclaimer.
 10 *     * Redistributions in binary form must reproduce the above copyright
 11 *       notice, this list of conditions and the following disclaimer in the
 12 *       documentation and/or other materials provided with the distribution.
 13 *     * Neither the name of RealDolmen nor the
 14 *       names of its contributors may be used to endorse or promote products
 15 *       derived from this software without specific prior written permission.
 16 *
 17 * THIS SOFTWARE IS PROVIDED BY RealDolmen ''AS IS'' AND ANY
 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 20 * DISCLAIMED. IN NO EVENT SHALL RealDolmen BE LIABLE FOR ANY
 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 27 *
 28 * @category   Microsoft
 29 * @package    Microsoft_WindowsAzure
 30 * @subpackage Storage
 31 * @copyright  Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
 32 * @license    http://todo     name_todo
 33 * @version    $Id: Blob.php 24241 2009-07-22 09:43:13Z unknown $
 34 */
 35
 36/**
 37 * @see Microsoft_WindowsAzure_SharedKeyCredentials
 38 */
 39require_once 'Microsoft/WindowsAzure/SharedKeyCredentials.php';
 40
 41/**
 42 * @see Microsoft_WindowsAzure_RetryPolicy
 43 */
 44require_once 'Microsoft/WindowsAzure/RetryPolicy.php';
 45
 46/**
 47 * @see Microsoft_Http_Transport
 48 */
 49require_once 'Microsoft/Http/Transport.php';
 50
 51/**
 52 * @see Microsoft_Http_Response
 53 */
 54require_once 'Microsoft/Http/Response.php';
 55
 56/**
 57 * @see Microsoft_WindowsAzure_Storage
 58 */
 59require_once 'Microsoft/WindowsAzure/Storage.php';
 60
 61/**
 62 * Microsoft_WindowsAzure_Storage_QueueInstance
 63 */
 64require_once 'Microsoft/WindowsAzure/Storage/QueueInstance.php';
 65
 66/**
 67 * Microsoft_WindowsAzure_Storage_QueueMessage
 68 */
 69require_once 'Microsoft/WindowsAzure/Storage/QueueMessage.php';
 70
 71/**
 72 * @see Microsoft_WindowsAzure_Exception
 73 */
 74require_once 'Microsoft/WindowsAzure/Exception.php';
 75
 76
 77/**
 78 * @category   Microsoft
 79 * @package    Microsoft_WindowsAzure
 80 * @subpackage Storage
 81 * @copyright  Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
 82 * @license    http://phpazure.codeplex.com/license
 83 */
 84class Microsoft_WindowsAzure_Storage_Queue extends Microsoft_WindowsAzure_Storage
 85{
 86	/**
 87	 * Maximal message size (in bytes)
 88	 */
 89	const MAX_MESSAGE_SIZE = 8388608;
 90	
 91	/**
 92	 * Maximal message ttl (in seconds)
 93	 */
 94	const MAX_MESSAGE_TTL = 604800;
 95	
 96	/**
 97	 * Creates a new Microsoft_WindowsAzure_Storage_Queue instance
 98	 *
 99	 * @param string $host Storage host name
100	 * @param string $accountName Account name for Windows Azure
101	 * @param string $accountKey Account key for Windows Azure
102	 * @param boolean $usePathStyleUri Use path-style URI's
103	 * @param Microsoft_WindowsAzure_RetryPolicy $retryPolicy Retry policy to use when making requests
104	 */
105	public function __construct($host = Microsoft_WindowsAzure_Storage::URL_DEV_QUEUE, $accountName = Microsoft_WindowsAzure_SharedKeyCredentials::DEVSTORE_ACCOUNT, $accountKey = Microsoft_WindowsAzure_SharedKeyCredentials::DEVSTORE_KEY, $usePathStyleUri = false, Microsoft_WindowsAzure_RetryPolicy $retryPolicy = null)
106	{
107		parent::__construct($host, $accountName, $accountKey, $usePathStyleUri, $retryPolicy);
108		
109		// API version
110		$this->_apiVersion = '2009-04-14';
111	}
112	
113	/**
114	 * Check if a queue exists
115	 * 
116	 * @param string $queueName Queue name
117	 * @return boolean
118	 */
119	public function queueExists($queueName = '')
120	{
121		if ($queueName === '')
122			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
123		if (!self::isValidQueueName($queueName))
124		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
125			
126		// List queues
127        $queues = $this->listQueues($queueName, 1);
128        foreach ($queues as $queue)
129        {
130            if ($queue->Name == $queueName)
131                return true;
132        }
133        
134        return false;
135	}
136	
137	/**
138	 * Create queue
139	 *
140	 * @param string $queueName Queue name
141	 * @param array  $metadata  Key/value pairs of meta data
142	 * @return object Queue properties
143	 * @throws Microsoft_WindowsAzure_Exception
144	 */
145	public function createQueue($queueName = '', $metadata = array())
146	{
147		if ($queueName === '')
148			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
149		if (!self::isValidQueueName($queueName))
150		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
151			
152		// Create metadata headers
153		$headers = array();
154		foreach ($metadata as $key => $value)
155		{
156		    $headers["x-ms-meta-" . strtolower($key)] = $value;
157		}
158		
159		// Perform request
160		$response = $this->performRequest($queueName, '', Microsoft_Http_Transport::VERB_PUT, $headers);			
161		if ($response->isSuccessful())
162		{
163		    return new Microsoft_WindowsAzure_Storage_QueueInstance(
164		        $queueName,
165		        $metadata
166		    );
167		}
168		else
169		{
170			throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response, 'Resource could not be accessed.'));
171		}
172	}
173	
174	/**
175	 * Get queue
176	 * 
177	 * @param string $queueName  Queue name
178	 * @return Microsoft_WindowsAzure_Storage_QueueInstance
179	 * @throws Microsoft_WindowsAzure_Exception
180	 */
181	public function getQueue($queueName = '')
182	{
183		if ($queueName === '')
184			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
185		if (!self::isValidQueueName($queueName))
186		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
187		    
188		// Perform request
189		$response = $this->performRequest($queueName, '?comp=metadata', Microsoft_Http_Transport::VERB_GET);	
190		if ($response->isSuccessful())
191		{
192		    // Parse metadata
193		    $metadata = array();
194		    foreach ($response->getHeaders() as $key => $value)
195		    {
196		        if (substr(strtolower($key), 0, 10) == "x-ms-meta-")
197		        {
198		            $metadata[str_replace("x-ms-meta-", '', strtolower($key))] = $value;
199		        }
200		    }
201
202		    // Return queue
203		    $queue = new Microsoft_WindowsAzure_Storage_QueueInstance(
204		        $queueName,
205		        $metadata
206		    );
207		    $queue->ApproximateMessageCount = intval($response->getHeader('x-ms-approximate-message-count'));
208		    return $queue;
209		}
210		else
211		{
212		    throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response, 'Resource could not be accessed.'));
213		}
214	}
215	
216	/**
217	 * Get queue metadata
218	 * 
219	 * @param string $queueName  Queue name
220	 * @return array Key/value pairs of meta data
221	 * @throws Microsoft_WindowsAzure_Exception
222	 */
223	public function getQueueMetadata($queueName = '')
224	{
225		if ($queueName === '')
226			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
227		if (!self::isValidQueueName($queueName))
228		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
229			
230	    return $this->getQueue($queueName)->Metadata;
231	}
232	
233	/**
234	 * Set queue metadata
235	 * 
236	 * 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.
237	 *
238	 * @param string $queueName  Queue name
239	 * @param array  $metadata       Key/value pairs of meta data
240	 * @throws Microsoft_WindowsAzure_Exception
241	 */
242	public function setQueueMetadata($queueName = '', $metadata = array())
243	{
244		if ($queueName === '')
245			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
246		if (!self::isValidQueueName($queueName))
247		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
248		if (count($metadata) == 0)
249		    return;
250		    
251		// Create metadata headers
252		$headers = array();
253		foreach ($metadata as $key => $value)
254		{
255		    $headers["x-ms-meta-" . strtolower($key)] = $value;
256		}
257		
258		// Perform request
259		$response = $this->performRequest($queueName, '?comp=metadata', Microsoft_Http_Transport::VERB_PUT, $headers);
260
261		if (!$response->isSuccessful())
262			throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response, 'Resource could not be accessed.'));
263	}
264	
265	/**
266	 * Delete queue
267	 *
268	 * @param string $queueName Queue name
269	 * @throws Microsoft_WindowsAzure_Exception
270	 */
271	public function deleteQueue($queueName = '')
272	{
273		if ($queueName === '')
274			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
275		if (!self::isValidQueueName($queueName))
276		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
277			
278		// Perform request
279		$response = $this->performRequest($queueName, '', Microsoft_Http_Transport::VERB_DELETE);
280		if (!$response->isSuccessful())
281			throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response, 'Resource could not be accessed.'));
282	}
283	
284	/**
285	 * List queues
286	 *
287	 * @param string $prefix     Optional. Filters the results to return only queues whose name begins with the specified prefix.
288	 * @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)
289	 * @param string $marker     Optional string value that identifies the portion of the list to be returned with the next list operation.
290	 * @param int    $currentResultCount Current result count (internal use)
291	 * @return array
292	 * @throws Microsoft_WindowsAzure_Exception
293	 */
294	public function listQueues($prefix = null, $maxResults = null, $marker = null, $currentResultCount = 0)
295	{
296	    // Build query string
297	    $queryString = '?comp=list';
298	    if (!is_null($prefix))
299	        $queryString .= '&prefix=' . $prefix;
300	    if (!is_null($maxResults))
301	        $queryString .= '&maxresults=' . $maxResults;
302	    if (!is_null($marker))
303	        $queryString .= '&marker=' . $marker;
304	        
305		// Perform request
306		$response = $this->performRequest('', $queryString, Microsoft_Http_Transport::VERB_GET);	
307		if ($response->isSuccessful())
308		{
309			$xmlQueues = $this->parseResponse($response)->Queues->Queue;
310			$xmlMarker = (string)$this->parseResponse($response)->NextMarker;
311
312			$queues = array();
313			if (!is_null($xmlQueues))
314			{
315				for ($i = 0; $i < count($xmlQueues); $i++)
316				{
317					$queues[] = new Microsoft_WindowsAzure_Storage_QueueInstance(
318						(string)$xmlQueues[$i]->QueueName
319					);
320				}
321			}
322			$currentResultCount = $currentResultCount + count($queues);
323			if (!is_null($maxResults) && $currentResultCount < $maxResults)
324			{
325    			if (!is_null($xmlMarker) && $xmlMarker != '')
326    			{
327    			    $queues = array_merge($queues, $this->listQueues($prefix, $maxResults, $xmlMarker, $currentResultCount));
328    			}
329			}
330			if (!is_null($maxResults) && count($queues) > $maxResults)
331			    $queues = array_slice($queues, 0, $maxResults);
332			    
333			return $queues;
334		}
335		else 
336		{
337			throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response, 'Resource could not be accessed.'));
338		}
339	}
340	
341	/**
342	 * Put message into queue
343	 *
344	 * @param string $queueName  Queue name
345	 * @param string $message    Message
346	 * @param int    $ttl        Message Time-To-Live (in seconds). Defaults to 7 days if the parameter is omitted.
347	 * @throws Microsoft_WindowsAzure_Exception
348	 */
349	public function putMessage($queueName = '', $message = '', $ttl = null)
350	{
351		if ($queueName === '')
352			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
353		if (!self::isValidQueueName($queueName))
354		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
355		if (strlen($message) > self::MAX_MESSAGE_SIZE)
356		    throw new Microsoft_WindowsAzure_Exception('Message is too big. Message content should be < 8KB.');
357		if ($message == '')
358		    throw new Microsoft_WindowsAzure_Exception('Message is not specified.');
359		if (!is_null($ttl) && ($ttl <= 0 || $ttl > self::MAX_MESSAGE_SIZE))
360		    throw new Microsoft_WindowsAzure_Exception('Message TTL is invalid. Maximal TTL is 7 days (' . self::MAX_MESSAGE_SIZE . ' seconds) and should be greater than zero.');
361		    
362	    // Build query string
363	    $queryString = '';
364	    if (!is_null($ttl))
365	        $queryString .= '?messagettl=' . $ttl;
366	        
367	    // Build body
368	    $rawData = '';
369	    $rawData .= '<QueueMessage>';
370	    $rawData .= '    <MessageText>' . base64_encode($message) . '</MessageText>';
371	    $rawData .= '</QueueMessage>';
372	        
373		// Perform request
374		$response = $this->performRequest($queueName . '/messages', $queryString, Microsoft_Http_Transport::VERB_POST, array(), false, $rawData);
375
376		if (!$response->isSuccessful())
377		{
378			throw new Microsoft_WindowsAzure_Exception('Error putting message into queue.');
379		}
380	}
381	
382	/**
383	 * Get queue messages
384	 *
385	 * @param string $queueName         Queue name
386	 * @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.
387	 * @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.
388	 * @param string $peek              Peek only?
389	 * @return array
390	 * @throws Microsoft_WindowsAzure_Exception
391	 */
392	public function getMessages($queueName = '', $numOfMessages = 1, $visibilityTimeout = null, $peek = false)
393	{
394		if ($queueName === '')
395			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
396		if (!self::isValidQueueName($queueName))
397		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
398		if ($numOfMessages < 1 || $numOfMessages > 32 || intval($numOfMessages) != $numOfMessages)
399		    throw new Microsoft_WindowsAzure_Exception('Invalid number of messages to retrieve.');
400		if (!is_null($visibilityTimeout) && ($visibilityTimeout <= 0 || $visibilityTimeout > 7200))
401		    throw new Microsoft_WindowsAzure_Exception('Visibility timeout is invalid. Maximum value is 2 hours (7200 seconds) and should be greater than zero.');
402		    
403	    // Build query string
404	    $query = array();
405    	if ($peek)
406    	    $query[] = 'peekonly=true';
407    	if ($numOfMessages > 1)
408	        $query[] = 'numofmessages=' . $numOfMessages;
409    	if (!$peek && !is_null($visibilityTimeout))
410	        $query[] = 'visibilitytimeout=' . $visibilityTimeout;   
411    	$queryString = '?' . implode('&', $query);
412	        
413		// Perform request
414		$response = $this->performRequest($queueName . '/messages', $queryString, Microsoft_Http_Transport::VERB_GET);	
415		if ($response->isSuccessful())
416		{
417		    // Parse results
418			$result = $this->parseResponse($response);
419		    if (!$result)
420		        return array();
421
422		    $xmlMessages = null;
423		    if (count($result->QueueMessage) > 1)
424    		{
425    		    $xmlMessages = $result->QueueMessage;
426    		}
427    		else
428    		{
429    		    $xmlMessages = array($result->QueueMessage);
430    		}
431
432			$messages = array();
433			for ($i = 0; $i < count($xmlMessages); $i++)
434			{
435				$messages[] = new Microsoft_WindowsAzure_Storage_QueueMessage(
436					(string)$xmlMessages[$i]->MessageId,
437					(string)$xmlMessages[$i]->InsertionTime,
438					(string)$xmlMessages[$i]->ExpirationTime,
439					($peek ? '' : (string)$xmlMessages[$i]->PopReceipt),
440					($peek ? '' : (string)$xmlMessages[$i]->TimeNextVisible),
441					base64_decode((string)$xmlMessages[$i]->MessageText)
442			    );
443			}
444			    
445			return $messages;
446		}
447		else 
448		{
449			throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response, 'Resource could not be accessed.'));
450		}
451	}
452	
453	/**
454	 * Peek queue messages
455	 *
456	 * @param string $queueName         Queue name
457	 * @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.
458	 * @return array
459	 * @throws Microsoft_WindowsAzure_Exception
460	 */
461	public function peekMessages($queueName = '', $numOfMessages = 1)
462	{
463	    return $this->getMessages($queueName, $numOfMessages, null, true);
464	}
465	
466	/**
467	 * Clear queue messages
468	 *
469	 * @param string $queueName         Queue name
470	 * @throws Microsoft_WindowsAzure_Exception
471	 */
472	public function clearMessages($queueName = '')
473	{
474		if ($queueName === '')
475			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
476		if (!self::isValidQueueName($queueName))
477		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
478
479		// Perform request
480		$response = $this->performRequest($queueName . '/messages', '', Microsoft_Http_Transport::VERB_DELETE);	
481		if (!$response->isSuccessful())
482		{
483			throw new Microsoft_WindowsAzure_Exception('Error clearing messages from queue.');
484		}
485	}
486	
487	/**
488	 * Delete queue message
489	 *
490	 * @param string $queueName         					Queue name
491	 * @param Microsoft_WindowsAzure_Storage_QueueMessage $message Message to delete from queue. A message retrieved using "peekMessages" can NOT be deleted!
492	 * @throws Microsoft_WindowsAzure_Exception
493	 */
494	public function deleteMessage($queueName = '', Microsoft_WindowsAzure_Storage_QueueMessage $message)
495	{
496		if ($queueName === '')
497			throw new Microsoft_WindowsAzure_Exception('Queue name is not specified.');
498		if (!self::isValidQueueName($queueName))
499		    throw new Microsoft_WindowsAzure_Exception('Queue name does not adhere to queue naming conventions. See http://msdn.microsoft.com/en-us/library/dd179349.aspx for more information.');
500		if ($message->PopReceipt == '')
501		    throw new Microsoft_WindowsAzure_Exception('A message retrieved using "peekMessages" can NOT be deleted! Use "getMessages" instead.');
502
503		// Perform request
504		$response = $this->performRequest($queueName . '/messages/' . $message->MessageId, '?popreceipt=' . $message->PopReceipt, Microsoft_Http_Transport::VERB_DELETE);	
505		if (!$response->isSuccessful())
506		{
507			throw new Microsoft_WindowsAzure_Exception($this->getErrorMessage($response, 'Resource could not be accessed.'));
508		}
509	}
510	
511	/**
512	 * Is valid queue name?
513	 *
514	 * @param string $queueName Queue name
515	 * @return boolean
516	 */
517    public static function isValidQueueName($queueName = '')
518    {
519        if (!ereg("^[a-z0-9][a-z0-9-]*$", $queueName))
520            return false;
521    
522        if (strpos($queueName, '--') !== false)
523            return false;
524    
525        if (strtolower($queueName) != $queueName)
526            return false;
527    
528        if (strlen($queueName) < 3 || strlen($queueName) > 63)
529            return false;
530            
531        if (substr($queueName, -1) == '-')
532            return false;
533    
534        return true;
535    }
536    
537	/**
538	 * Get error message from Microsoft_Http_Response
539	 * 
540	 * @param Microsoft_Http_Response $response Repsonse
541	 * @param string $alternativeError Alternative error message
542	 * @return string
543	 */
544	protected function getErrorMessage(Microsoft_Http_Response $response, $alternativeError = 'Unknown error.')
545	{
546		$response = $this->parseResponse($response);
547		if ($response && $response->Message)
548		    return (string)$response->Message;
549		else
550		    return $alternativeError;
551	}
552}