1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.saml2.metadata.provider;
18
19 import java.util.Timer;
20 import java.util.TimerTask;
21
22 import org.joda.time.DateTime;
23 import org.opensaml.saml2.common.SAML2Helper;
24 import org.opensaml.util.resource.Resource;
25 import org.opensaml.util.resource.ResourceException;
26 import org.opensaml.xml.XMLObject;
27 import org.opensaml.xml.io.UnmarshallingException;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31
32
33
34
35
36 public class ResourceBackedMetadataProvider extends AbstractObservableMetadataProvider {
37
38
39 private final Logger log = LoggerFactory.getLogger(ResourceBackedMetadataProvider.class);
40
41
42 private Timer taskTimer;
43
44
45 private long maxCacheDuration;
46
47
48 private Resource metadataResource;
49
50
51 private DateTime lastMetadataUpdate;
52
53
54 private boolean maintainExpiredMetadata;
55
56
57 private XMLObject cachedMetadata;
58
59
60
61
62
63
64
65
66
67
68
69 public ResourceBackedMetadataProvider(Resource resource, Timer timer, long maxMetadataCacheDuration)
70 throws MetadataProviderException {
71 super();
72
73 taskTimer = timer;
74
75 if (maxMetadataCacheDuration < 1) {
76 throw new IllegalArgumentException("Max cache duration must be a positive number");
77 }
78 maxCacheDuration = maxMetadataCacheDuration;
79
80 try {
81 if (!resource.exists()) {
82 throw new MetadataProviderException("Resource " + resource.getLocation() + " does not exist.");
83 }
84 metadataResource = resource;
85 maintainExpiredMetadata = false;
86 } catch (ResourceException e) {
87 throw new MetadataProviderException("Unable to read resource", e);
88 }
89 }
90
91
92
93
94
95
96 public void initialize() throws MetadataProviderException {
97 refreshMetadata();
98 }
99
100
101
102
103
104
105 public boolean maintainExpiredMetadata() {
106 return maintainExpiredMetadata;
107 }
108
109
110
111
112
113
114 public void setMaintainExpiredMetadata(boolean maintain) {
115 maintainExpiredMetadata = maintain;
116 }
117
118
119 public XMLObject getMetadata() throws MetadataProviderException {
120 return cachedMetadata;
121 }
122
123
124
125
126
127
128 private void refreshMetadata() throws MetadataProviderException {
129 try {
130 boolean metadataChanged = false;
131
132 XMLObject metadata = getLatestMetadata();
133 if(metadata != cachedMetadata){
134 metadataChanged = true;
135 }
136
137
138 DateTime expirationTime = SAML2Helper.getEarliestExpiration(metadata);
139 if (expirationTime != null && !maintainExpiredMetadata() && expirationTime.isBeforeNow()) {
140 log.debug("Metadata from resource {} is expired and this provider is configured not to retain expired metadata.",
141 metadataResource.getInputStream());
142 cachedMetadata = null;
143 metadataChanged = true;
144 } else {
145 cachedMetadata = metadata;
146 }
147
148
149 if (metadataChanged) {
150 emitChangeEvent();
151 }
152
153
154 long nextUpdateDelay=0;
155 if (expirationTime != null && expirationTime.isBefore(System.currentTimeMillis() + maxCacheDuration)) {
156 nextUpdateDelay = expirationTime.getMillis() - System.currentTimeMillis();
157 }
158
159 if(nextUpdateDelay <= 0){
160 nextUpdateDelay = maxCacheDuration;
161 }
162
163 log.debug("Next refresh of metadata from resource {} scheduled in {}ms", metadataResource.getLocation(),
164 nextUpdateDelay);
165 taskTimer.schedule(new MetadataPollTask(), nextUpdateDelay);
166 } catch (ResourceException e) {
167 String errorMsg = "Unable to read metadata file";
168 log.error(errorMsg, e);
169 throw new MetadataProviderException(errorMsg, e);
170 }
171 }
172
173
174
175
176
177
178
179
180
181
182 private XMLObject getLatestMetadata() throws MetadataProviderException {
183 XMLObject metadata;
184
185 try {
186 DateTime metadataUpdateTime = metadataResource.getLastModifiedTime();
187 if (lastMetadataUpdate == null || metadataUpdateTime.isAfter(lastMetadataUpdate)) {
188 lastMetadataUpdate = metadataUpdateTime;
189 log.debug("Refreshing metadata from resource {}", metadataResource.getLocation());
190 metadata = unmarshallMetadata(metadataResource.getInputStream());
191 filterMetadata(metadata);
192 releaseMetadataDOM(metadata);
193 } else {
194 metadata = cachedMetadata;
195 }
196
197 return metadata;
198 } catch (ResourceException e) {
199 String errorMsg = "Unable to read metadata file";
200 log.error(errorMsg, e);
201 throw new MetadataProviderException(errorMsg, e);
202 } catch (UnmarshallingException e) {
203 String errorMsg = "Unable to unmarshall metadata";
204 log.error(errorMsg, e);
205 throw new MetadataProviderException(errorMsg, e);
206 } catch (FilterException e) {
207 String errorMsg = "Unable to filter metadata";
208 log.error(errorMsg, e);
209 throw new MetadataProviderException(errorMsg, e);
210 }
211 }
212
213
214 private class MetadataPollTask extends TimerTask {
215
216
217 public void run() {
218 try {
219 log.debug("Checking if metadata from resource {} should be updated", metadataResource.getLocation());
220 refreshMetadata();
221 } catch (MetadataProviderException e) {
222 log.error("Unable to refresh metadata", e);
223 }
224 }
225 }
226 }