1 package de.juplo.httpresources;
2
3 import org.springframework.http.HttpHeaders;
4 import org.springframework.util.MimeType;
5
6 import java.io.*;
7 import java.net.URI;
8 import java.net.URL;
9 import java.time.Clock;
10 import java.util.Objects;
11
12
13 public class HttpResource implements org.springframework.web.servlet.resource.HttpResource
14 {
15 final HttpResources resources;
16 final HttpResourceFetcher fetcher;
17 final Clock clock;
18 final URI uri;
19
20 HttpData data;
21 boolean modified;
22
23 HttpResource(
24 HttpResources resources,
25 HttpResourceFetcher fetcher,
26 Clock clock,
27 URI uri)
28 {
29 this(resources, fetcher, clock, uri, HttpData.NOT_FETCHED);
30 }
31
32 HttpResource(
33 HttpResources resources,
34 HttpResourceFetcher fetcher,
35 Clock clock,
36 URI uri,
37 HttpData data)
38 {
39 this.resources = resources;
40 this.fetcher = fetcher;
41 this.clock = clock;
42 this.uri = uri;
43 this.data = data;
44 }
45
46
47 synchronized public long expires()
48 {
49 return data.expires;
50 }
51
52 synchronized public boolean isExpired()
53 {
54 return data.expires < clock.millis();
55 }
56
57 @Override
58 synchronized public boolean exists()
59 {
60 if (isExpired())
61 fetch();
62 return data.content != null;
63 }
64
65 @Override
66 synchronized public boolean isReadable()
67 {
68 return exists();
69 }
70
71 @Override
72 public boolean isOpen()
73 {
74 return false;
75 }
76
77 @Override
78 public URL getURL() throws IOException
79 {
80 return uri.toURL();
81 }
82
83 @Override
84 public URI getURI()
85 {
86 return uri;
87 }
88
89 @Override
90 public File getFile() throws IOException
91 {
92 throw new FileNotFoundException("The resource " + getDescription() + " cannot be retrived as file");
93 }
94
95 @Override
96 synchronized public long contentLength() throws IOException
97 {
98 if (!fetched())
99 fetch();
100
101 if (data.content == null)
102 throw new FileNotFoundException("The resource " + getDescription() + " cannot be retrived");
103
104 return data.content.length;
105 }
106
107 synchronized public MimeType contentType()
108 {
109 return data.headers.getContentType();
110 }
111
112 @Override
113 synchronized public long lastModified() throws IOException
114 {
115 if (!fetched())
116 fetch();
117
118 if (data.content == null)
119 throw new FileNotFoundException("The resource " + getDescription() + " cannot be retrived");
120
121 return data.lastModified;
122 }
123
124 synchronized public String eTag()
125 {
126 return data.eTag;
127 }
128
129 @Override
130 public HttpResource createRelative(String relativePath) throws IOException
131 {
132 try
133 {
134 return createRelative(HttpResources.convert(relativePath));
135 }
136 catch (IllegalArgumentException e)
137 {
138 throw new IOException("Invalid relative path: " + relativePath, e);
139 }
140 }
141
142 public HttpResource createRelative(URI relative) throws IOException
143 {
144 return resources.getResource(HttpResources.resolve(relative, uri));
145 }
146
147 @Override
148 public String getFilename()
149 {
150 return uri.getPath();
151 }
152
153 @Override
154 public String getDescription()
155 {
156 return "HTTP [" + this.uri + "]";
157 }
158
159 @Override
160 synchronized public InputStream getInputStream() throws IOException
161 {
162 if (!fetched())
163 {
164 fetch();
165 }
166 else
167 {
168 isModified();
169 }
170
171
172 modified = false;
173
174 if (data.content == null)
175 throw new FileNotFoundException("The resource " + getDescription() + " cannot be retrived as file");
176
177 return new ByteArrayInputStream(data.content);
178 }
179
180 synchronized public boolean isModified()
181 {
182 if (modified)
183 {
184 return true;
185 }
186
187 if (!isExpired())
188 {
189 return false;
190 }
191
192 return fetch();
193 }
194
195
196
197
198
199
200
201 synchronized public boolean fetched()
202 {
203 return data.expires != Long.MIN_VALUE;
204 }
205
206
207
208
209
210
211
212
213
214
215 synchronized public boolean fetch()
216 {
217 HttpData result = fetcher.fetch(uri, data);
218
219
220
221 modified = !data.equals(result);
222
223 data = result;
224
225 return modified;
226 }
227
228 @Override
229 synchronized public HttpHeaders getResponseHeaders()
230 {
231 return data.headers;
232 }
233
234
235 @Override
236 public int hashCode()
237 {
238 return uri.hashCode();
239 }
240
241 @Override
242 public boolean equals(Object o)
243 {
244 if (o == null)
245 return false;
246 if (o == this)
247 return true;
248 if (!(o instanceof HttpResource))
249 return false;
250 return Objects.equals(uri, ((HttpResource) o).uri);
251 }
252
253 @Override
254 public String toString()
255 {
256 return getDescription();
257 }
258 }