View Javadoc

1   /*
2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.saml2.metadata.provider;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileWriter;
22  import java.io.IOException;
23  
24  import org.opensaml.Configuration;
25  import org.opensaml.xml.XMLObject;
26  import org.opensaml.xml.io.Marshaller;
27  import org.opensaml.xml.io.MarshallingException;
28  import org.opensaml.xml.io.UnmarshallingException;
29  import org.opensaml.xml.util.XMLHelper;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  import org.w3c.dom.Element;
33  
34  /**
35   * A URL metadata provider that caches a copy of the retrieved metadata to disk so that, in the event that the metadata
36   * may not be pulled from the URL it may be pulled from disk using the last fetched data. If the backing file does not
37   * already exist it will be created.
38   * 
39   * It is the responsibility of the caller to re-initialize, via {@link #initialize()}, if any properties of this
40   * provider are changed.
41   */
42  public class FileBackedHTTPMetadataProvider extends HTTPMetadataProvider {
43  
44      /** Class logger. */
45      private final Logger log = LoggerFactory.getLogger(FileBackedHTTPMetadataProvider.class);
46  
47      /** File containing the backup of the metadata. */
48      private File metadataBackupFile;
49  
50      /**
51       * Constructor.
52       * 
53       * @param metadataURL the URL to fetch the metadata
54       * @param requestTimeout the time, in milliseconds, to wait for the metadata server to respond
55       * @param backingFilePath the file that will keep a backup copy of the metadata,
56       * 
57       * @throws MetadataProviderException thrown if the URL is not a valid URL, the metadata can not be retrieved from
58       *             the URL, the given file can not be created or written to
59       */
60      public FileBackedHTTPMetadataProvider(String metadataURL, int requestTimeout, String backingFilePath)
61              throws MetadataProviderException {
62          super(metadataURL, requestTimeout);
63  
64          metadataBackupFile = new File(backingFilePath);
65          if (metadataBackupFile.exists()) {
66              if (metadataBackupFile.isDirectory()) {
67                  throw new MetadataProviderException("Filepath " + backingFilePath
68                          + " is a directory and may not be used as a backing metadata file");
69              }
70              if (!metadataBackupFile.canRead()) {
71                  throw new MetadataProviderException("Filepath " + backingFilePath
72                          + " exists but can not be read by this user");
73              }
74              if (!metadataBackupFile.canWrite()) {
75                  throw new MetadataProviderException("Filepath " + backingFilePath
76                          + " exists but can not be written to by this user");
77              }
78          } else {
79              try {
80                  metadataBackupFile.createNewFile();
81              } catch (IOException e) {
82                  log.error("Unable to create backing file " + backingFilePath, e);
83                  throw new MetadataProviderException("Unable to create backing file " + backingFilePath, e);
84              }
85          }
86      }
87  
88      /**
89       * Fetches the metadata from the remote server or from the local filesystem if it can not be retrieved remotely.
90       * 
91       * @return the unmarshalled metadata
92       * 
93       * @throws IOException thrown if the metadata can not be fetched from the remote server or local filesystems
94       * @throws UnmarshallingException thrown if the metadata can not be unmarshalled
95       */
96      protected XMLObject fetchMetadata() throws IOException, UnmarshallingException {
97          XMLObject metadata;
98          try {
99              metadata = super.fetchMetadata();
100         } catch (Exception e) {
101             log.warn("Unable to read metadata from " + getMetadataURI() + " attempting to read it from local backup", e);
102             return getLocalMetadata();
103         }
104 
105         // If we read the metadata from the remote server then write it to disk
106         log.debug("Writting retrieved metadata to backup file {}", metadataBackupFile.getAbsolutePath());
107         try {
108             writeMetadataToFile(metadata);
109         } catch (Exception e) {
110             log.error("Unable to write metadata to backup file", e);
111             throw new IOException("Unable to write metadata to backup file: " + e.getMessage());
112         }
113 
114         return metadata;
115     }
116 
117     /**
118      * Reads filtered metadata from the backup file.
119      * 
120      * @return cached copy of the metadata read from disk
121      * 
122      * @throws IOException thrown if the metadata can not be read from disk
123      * @throws UnmarshallingException thrown if the metadata, read from disk, can not be unmarshalled
124      */
125     protected XMLObject getLocalMetadata() throws IOException, UnmarshallingException {
126         if (!(metadataBackupFile.exists() && metadataBackupFile.canRead())) {
127             throw new IOException("Unable to read metadata from backup file " + metadataBackupFile.getAbsolutePath());
128         }
129         return unmarshallMetadata(new FileInputStream(metadataBackupFile));
130     }
131 
132     /**
133      * Writes the currently cached metadata to file.
134      * 
135      * @param metadata metadata to write to disk
136      * 
137      * @throws MetadataProviderException thrown if metadata can not be written to disk
138      */
139     protected void writeMetadataToFile(XMLObject metadata) throws MetadataProviderException {
140         if (!metadataBackupFile.canWrite()) {
141             throw new MetadataProviderException("Unable to write to metadata backup file "
142                     + metadataBackupFile.getAbsolutePath());
143         }
144 
145         try {
146             Element metadataElement;
147 
148             // The metadata object should still have its DOM
149             // but we'll create it if it doesn't
150             if (metadata.getDOM() != null) {
151                 metadataElement = metadata.getDOM();
152             } else {
153                 Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(metadata);
154                 metadataElement = marshaller.marshall(metadata);
155             }
156 
157             if (log.isDebugEnabled()) {
158                 log.debug("Converting DOM to a string");
159             }
160             XMLHelper.writeNode(metadataElement, new FileWriter(metadataBackupFile));
161         } catch (IOException e) {
162             log.error("Unable to write metadata to file " + metadataBackupFile.getAbsolutePath(), e);
163             throw new MetadataProviderException("Unable to write metadata to file");
164         } catch (MarshallingException e) {
165             log.error("Unable to marshall metadata in order to write it to file", e);
166             throw new MetadataProviderException("Unable to marshall metadata in order to write it to file");
167         }
168     }
169 }