diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
index 60003b57ac7b59fdcb8d023ce98e9c8bb45fc83d..7404de95ff4535a17baeacc2120732149b12a383 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
@@ -12,74 +12,43 @@
 class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Metadata_MetaDataStorageSource {
 
 	/**
-	 * This variable contains an associative array with the parsed metadata.
+	 * The cache directory, or NULL if no cache directory is configured.
 	 */
-	private $metadata;
-	private $config;
+	private $cacheDir;
+
+
+	/**
+	 * The maximum cache length, in seconds.
+	 */
+	private $cacheLength;
+
 
 	/**
-	 * This function initializes the XML metadata source. The configuration must contain one of
-	 * the following options:
-	 * - 'file': Path to a file with the metadata. This path is relative to the simpleSAMLphp
-	 *           base directory.
-	 * - 'url': URL we should download the metadata from. This is only meant for testing.
+	 * This function initializes the dynamic XML metadata source.
+	 *
+	 * Options:
+	 * - 'cachedir':  Directory where metadata can be cached. Optional.
+	 * - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
+	 *                  hours (86400 seconds).
 	 *
-	 * @param $config  The configuration for this instance of the XML metadata source.
+	 * @param array $config  The configuration for this instance of the XML metadata source.
 	 */
 	protected function __construct($config) {
+		assert('is_array($config)');
 
-		$this->config = $config;
-	
-		/* Get the configuration. 
-		$globalConfig = SimpleSAML_Configuration::getInstance();
-
-		if(array_key_exists('cache', $config)) {
-			$src = $globalConfig->resolvePath($config['file']);
-		} elseif(array_key_exists('url', $config)) {
-			$src = $config['url'];
+		if (array_key_exists('cachedir', $config)) {
+			$globalConfig = SimpleSAML_Configuration::getInstance();
+			$this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
 		} else {
-			throw new Exception('Missing either \'file\' or \'url\' in XML metadata source configuration.');
+			$this->cacheDir = NULL;
 		}
 
-
-		$SP1x = array();
-		$IdP1x = array();
-		$SP20 = array();
-		$IdP20 = array();
-
-		$entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsFile($src);
-		foreach($entities as $entityId => $entity) {
-
-			$md = $entity->getMetadata1xSP();
-			if($md !== NULL) {
-				$SP1x[$entityId] = $md;
-			}
-
-			$md = $entity->getMetadata1xIdP();
-			if($md !== NULL) {
-				$IdP1x[$entityId] = $md;
-			}
-
-			$md = $entity->getMetadata20SP();
-			if($md !== NULL) {
-				$SP20[$entityId] = $md;
-			}
-
-			$md = $entity->getMetadata20IdP();
-			if($md !== NULL) {
-				$IdP20[$entityId] = $md;
-			}
-
+		if (array_key_exists('cachelength', $config)) {
+			$this->cacheLength = $config['cachelength'];
+		} else {
+			$this->cacheLength = 86400;
 		}
 
-		$this->metadata = array(
-			'shib13-sp-remote' => $SP1x,
-			'shib13-idp-remote' => $IdP1x,
-			'saml20-sp-remote' => $SP20,
-			'saml20-idp-remote' => $IdP20,
-			);
-		*/
-
 	}
 
 
@@ -91,40 +60,130 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Me
 	 * @return An associative array with all entities in the given set.
 	 */
 	public function getMetadataSet($set) {
-		/*
-		if(array_key_exists($set, $this->metadata)) {
-			return $this->metadata[$set];
-		}
-		*/
 
 		/* We don't have this metadata set. */
 		return array();
 	}
-	
-	
-	private function getCacheFilename($entityId) {
+
+
+	/**
+	 * Find the cache file name for an entity,
+	 *
+	 * @param string $set  The metadata set this entity belongs to.
+	 * @param string $entityId  The entity id of this entity.
+	 * @return string  The full path to the cache file.
+	 */
+	private function getCacheFilename($set, $entityId) {
+		assert('is_string($set)');
+		assert('is_string($entityId)');
+
 		$cachekey = sha1($entityId);
 		$globalConfig = SimpleSAML_Configuration::getInstance();
-		return $globalConfig->resolvePath($this->config['cachedir']) . '/' . $cachekey . '.cached.xml';
+		return $this->cacheDir . '/' . $set . '-' . $cachekey . '.cached.xml';
 	}
-	
-	
-	private function getFromCache($entityId) {
-		$cachefilename = $this->getCacheFilename($entityId);
+
+
+	/**
+	 * Load a entity from the cache.
+	 *
+	 * @param string $set  The metadata set this entity belongs to.
+	 * @param string $entityId  The entity id of this entity.
+	 * @return array|NULL  The associative array with the metadata for this entity, or NULL
+	 *                     if the entity could not be found.
+	 */
+	private function getFromCache($set, $entityId) {
+		assert('is_string($set)');
+		assert('is_string($entityId)');
+
+		if (empty($this->cacheDir)) {
+			return NULL;
+		}
+
+		$cachefilename = $this->getCacheFilename($set, $entityId);
 		if (!file_exists($cachefilename)) return NULL;
 		if (!is_readable($cachefilename)) throw new Exception('Could not read cache file for entity [' . $cachefilename. ']');
 		SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Reading cache [' . $entityId . '] => [' . $cachefilename . ']' );
-		return file_get_contents($cachefilename);		
+
+		/* Ensure that this metadata isn't older that the cachelength option allows. This
+		 * must be verified based on the file, since this option may be changed after the
+		 * file is written.
+		 */
+		$stat = stat($cachefilename);
+		if ($stat['mtime'] + $this->cacheLength <= time()) {
+			SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Cache file older that the cachelength option allows.');
+			return NULL;
+		}
+
+		$rawData = file_get_contents($cachefilename);
+		if (empty($rawData)) {
+			throw new Exception('Error reading metadata from cache file "' . $cachefilename . '": ' .
+				SimpleSAML_Utilities::getLastError());
+		}
+
+		$data = unserialize($rawData);
+		if ($data === FALSE) {
+			throw new Exception('Error deserializing cached data from file "' . $cachefilename .'".');
+		}
+
+		if (!is_array($data)) {
+			throw new Exception('Cached metadata from "' . $cachefilename . '" wasn\'t an array.');
+		}
+
+		return $data;
 	}
-	
-	private function writeToCache($entityId, $xmldata) {
-		$cachefilename = $this->getCacheFilename($entityId);
+
+
+	/**
+	 * Save a entity to the cache.
+	 *
+	 * @param string $set  The metadata set this entity belongs to.
+	 * @param string $entityId  The entity id of this entity.
+	 * @param array $data  The associative array with the metadata for this entity.
+	 */
+	private function writeToCache($set, $entityId, $data) {
+		assert('is_string($set)');
+		assert('is_string($entityId)');
+		assert('is_array($data)');
+
+		if (empty($this->cacheDir)) {
+			return;
+		}
+
+		$cachefilename = $this->getCacheFilename($set, $entityId);
 		if (!is_writable(dirname($cachefilename))) throw new Exception('Could not write cache file for entity [' . $cachefilename. ']');
 		SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Writing cache [' . $entityId . '] => [' . $cachefilename . ']' );
-		file_put_contents($cachefilename, $xmldata);
+		file_put_contents($cachefilename, serialize($data));
 	}
-	
-	
+
+
+	/**
+	 * Retrieve metadata for the correct set from a SAML2Parser.
+	 *
+	 * @param SimpleSAML_Metadata_SAMLParser $entity  A SAML2Parser representing an entity.
+	 * @param string $set  The metadata set we are looking for.
+	 * @return array|NULL  The associative array with the metadata, or NULL if no metadata for
+	 *                     the given set was found.
+	 */
+	private static function getParsedSet(SimpleSAML_Metadata_SAMLParser $entity, $set) {
+		assert('is_string($set)');
+
+		switch($set) {
+		case 'saml20-idp-remote':
+			return $entity->getMetadata20IdP();
+		case 'saml20-sp-remote':
+			return $entity->getMetadata20SP();
+		case 'shib13-idp-remote':
+			return $entity->getMetadata1xIdP();
+		case 'shib13-sp-remote':
+			return $entity->getMetadata1xSP();
+		default:
+			SimpleSAML_Logger::warning('MetaData - Handler.DynamicXML: Unknown metadata set: ' . $set);
+		}
+
+		return NULL;
+	}
+
+
 	/**
 	 * Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
 	 *
@@ -141,77 +200,73 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Me
 	 *         locate the entity.
 	 */
 	public function getMetaData($index, $set) {
-		
-		
-		SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: Loading metadata entity [' . $index . '] from [' . $set . ']' );
-
-		
-		/* Get the configuration. */
-		$globalConfig = SimpleSAML_Configuration::getInstance();
+		assert('is_string($index)');
+		assert('is_string($set)');
 
 		if (!preg_match('@(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@', $index)) {
 			SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: EntityID/index [' . $index . '] does not look like an URL. Skipping.' );
 			return NULL;
 		}
 
-		$xmldata = NULL;
+		SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: Loading metadata entity [' . $index . '] from [' . $set . ']' );
 
-		/**
-		 * Read from cache if cache is defined.
-		 */
-		if (!empty($this->config['cachedir'])) {
-			$xmldata = $this->getFromCache($index);
+		/* Read from cache if possible. */
+		$data = $this->getFromCache($set, $index);
+
+		if ($data !== NULL && array_key_exists('expires', $data) && $data['expires'] < time()) {
+			/* Metadata has expired. */
+			$data = NULL;
+		}
+
+		if (isset($data)) {
+			/* Metadata found in cache and not expired. */
+			SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Using cached metadata.');
+			return $data;
 		}
 
+		SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Downloading [' . $index . ']' );
+		$xmldata = file_get_contents($index);
 		if (empty($xmldata)) {
-			$xmldata = file_get_contents($index);
-		
-			if (!empty($this->config['cachedir'])) {
-				$this->writeToCache($index, $xmldata);
-			}
+			throw new Exception('Error downloading metadata from "' . $index . '": ' .
+				SimpleSAML_Utilities::getLastError());
 		}
 
-		
 		$entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsString($xmldata);
+		SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Completed parsing of [' .
+			$index . '] Found [' . count($entities). '] entries.' );
 
+		if (count($entities) === 0) {
+			throw new Exception('No entities found in "' . $index . '".');
+		}
 
-		SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Completed parsing of [' . $index . '] Found [' . count($entities). '] entries.' );
-
-		foreach($entities as $entityId => $entity) {
-
-			SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Looking for [' . $index . '] found [' . $entityId . '] entries.' );
-		
-			switch($set) {
-				case 'saml20-idp-remote' : 
-					$md = $entity->getMetadata20IdP();
-					if ($md !== NULL) return $md;
-					break;
-
-				case 'saml20-sp-remote' : 
-					$md = $entity->getMetadata20SP();
-					if ($md !== NULL) return $md;
-					break;
-
-				case 'shib13-idp-remote' : 
-					$md = $entity->getMetadata1xIdP();
-					if ($md !== NULL) return $md;
-					break;
-
-				case 'shib13-sp-remote' : 
-					$md = $entity->getMetadata1xSP();
-					if ($md !== NULL) return $md;
+		if (array_key_exists($index, $entities)) {
+			$entity = $entities[$index];
+			$data = self::getParsedSet($entity, $set);
+			if ($data === NULL) {
+				throw new Exception('No metadata for set "' . $set .
+					'" available from "' . $index . '".');
+			}
+		} else {
+			SimpleSAML_Logger::warning('MetaData - Handler.DynamicXML: No entity with correct' .
+				' entity id found. Using the first entity which defines the correct' .
+				' metadata.');
+			foreach ($entities as $entity) {
+				$data = self::getParsedSet($entity, $set);
+				if ($data !== NULL) {
 					break;
-					
+				}
 			}
+			if ($data === NULL) {
+				throw new Exception('No entities defines metadata for set "' .
+					$set . '" in "' . $index . '".');
+			}
+		}
 
+		$this->writeToCache($set, $index, $data);
 
-		}
-		
-		return NULL;
-		
+		return $data;
 	}
-	
-	
+
 }
 
 ?>
\ No newline at end of file