/* * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opensaml.saml2.metadata.provider; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import org.opensaml.Configuration; import org.opensaml.xml.XMLObject; import org.opensaml.xml.io.Marshaller; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.io.UnmarshallingException; import org.opensaml.xml.util.XMLHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; /** * A URL metadata provider that caches a copy of the retrieved metadata to disk so that, in the event that the metadata * may not be pulled from the URL it may be pulled from disk using the last fetched data. If the backing file does not * already exist it will be created. * * It is the responsibility of the caller to re-initialize, via {@link #initialize()}, if any properties of this * provider are changed. */ public class FileBackedHTTPMetadataProvider extends HTTPMetadataProvider { /** Class logger. */ private final Logger log = LoggerFactory.getLogger(FileBackedHTTPMetadataProvider.class); /** File containing the backup of the metadata. */ private File metadataBackupFile; /** * Constructor. * * @param metadataURL the URL to fetch the metadata * @param requestTimeout the time, in milliseconds, to wait for the metadata server to respond * @param backingFilePath the file that will keep a backup copy of the metadata, * * @throws MetadataProviderException thrown if the URL is not a valid URL, the metadata can not be retrieved from * the URL, the given file can not be created or written to */ public FileBackedHTTPMetadataProvider(String metadataURL, int requestTimeout, String backingFilePath) throws MetadataProviderException { super(metadataURL, requestTimeout); metadataBackupFile = new File(backingFilePath); if (metadataBackupFile.exists()) { if (metadataBackupFile.isDirectory()) { throw new MetadataProviderException("Filepath " + backingFilePath + " is a directory and may not be used as a backing metadata file"); } if (!metadataBackupFile.canRead()) { throw new MetadataProviderException("Filepath " + backingFilePath + " exists but can not be read by this user"); } if (!metadataBackupFile.canWrite()) { throw new MetadataProviderException("Filepath " + backingFilePath + " exists but can not be written to by this user"); } } else { try { metadataBackupFile.createNewFile(); } catch (IOException e) { log.error("Unable to create backing file " + backingFilePath, e); throw new MetadataProviderException("Unable to create backing file " + backingFilePath, e); } } } /** * Fetches the metadata from the remote server or from the local filesystem if it can not be retrieved remotely. * * @return the unmarshalled metadata * * @throws IOException thrown if the metadata can not be fetched from the remote server or local filesystems * @throws UnmarshallingException thrown if the metadata can not be unmarshalled */ protected XMLObject fetchMetadata() throws IOException, UnmarshallingException { XMLObject metadata; try { metadata = super.fetchMetadata(); } catch (Exception e) { log.warn("Unable to read metadata from remote server, attempting to read it from local backup", e); return getLocalMetadata(); } // If we read the metadata from the remote server then write it to disk log.debug("Writting retrieved metadata to backup file {}", metadataBackupFile.getAbsolutePath()); try { writeMetadataToFile(metadata); } catch (Exception e) { log.error("Unable to write metadata to backup file", e); throw new IOException("Unable to write metadata to backup file: " + e.getMessage()); } return metadata; } /** * Reads filtered metadata from the backup file. * * @return cached copy of the metadata read from disk * * @throws IOException thrown if the metadata can not be read from disk * @throws UnmarshallingException thrown if the metadata, read from disk, can not be unmarshalled */ protected XMLObject getLocalMetadata() throws IOException, UnmarshallingException { if (!(metadataBackupFile.exists() && metadataBackupFile.canRead())) { throw new IOException("Unable to read metadata from backup file " + metadataBackupFile.getAbsolutePath()); } return unmarshallMetadata(new FileInputStream(metadataBackupFile)); } /** * Writes the currently cached metadata to file. * * @param metadata metadata to write to disk * * @throws MetadataProviderException thrown if metadata can not be written to disk */ protected void writeMetadataToFile(XMLObject metadata) throws MetadataProviderException { if (!metadataBackupFile.canWrite()) { throw new MetadataProviderException("Unable to write to metadata backup file " + metadataBackupFile.getAbsolutePath()); } try { Element metadataElement; // The metadata object should still have its DOM // but we'll create it if it doesn't if (metadata.getDOM() != null) { metadataElement = metadata.getDOM(); } else { Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(metadata); metadataElement = marshaller.marshall(metadata); } if (log.isDebugEnabled()) { log.debug("Converting DOM to a string"); } XMLHelper.writeNode(metadataElement, new FileWriter(metadataBackupFile)); } catch (IOException e) { log.error("Unable to write metadata to file " + metadataBackupFile.getAbsolutePath(), e); throw new MetadataProviderException("Unable to write metadata to file"); } catch (MarshallingException e) { log.error("Unable to marshall metadata in order to write it to file", e); throw new MetadataProviderException("Unable to marshall metadata in order to write it to file"); } } }