blob: 75b9243fd7bad5c20e72aed45a33f07a8cca8215 [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001package aQute.lib.deployer.obr;
2
3import java.io.File;
4import java.io.FileNotFoundException;
5import java.io.FileOutputStream;
6import java.io.IOException;
7import java.io.InputStream;
8import java.io.OutputStream;
9import java.io.UnsupportedEncodingException;
10import java.net.URL;
11import java.net.URLEncoder;
12
13import aQute.bnd.service.ResourceHandle;
14
15public class URLResourceHandle implements ResourceHandle {
16
17 static final String FILE_SCHEME = "file:";
18 static final String HTTP_SCHEME = "http:";
19
20 final File cacheDir;
21
22 // The resolved, absolute URL of the resource
23 final URL url;
24
25 // The local file, if the resource IS a file, otherwise null.
26 final File localFile;
27
28 // The cached file copy of the resource, if it is remote and has been downloaded.
29 final File cachedFile;
30
31 public URLResourceHandle(String url, String baseUrl, final File cacheDir) throws IOException {
32 this.cacheDir = cacheDir;
33 if (url.startsWith(FILE_SCHEME)) {
34 // File URL may be relative or absolute
35 File file = new File(url.substring(FILE_SCHEME.length()));
36 if (file.isAbsolute()) {
37 this.localFile = file;
38 } else {
39 if (!baseUrl.startsWith(FILE_SCHEME))
40 throw new IllegalArgumentException("Relative file URLs cannot be resolved if the base URL is a non-file URL.");
41 this.localFile = resolveFile(baseUrl.substring(FILE_SCHEME.length()), file.toString());
42 }
43 this.url = localFile.toURI().toURL();
44 if (!localFile.isFile() && !localFile.isDirectory())
45 throw new FileNotFoundException("File URL " + this.url + " points at a non-existing file.");
46 this.cachedFile = null;
47 } else if (url.startsWith(HTTP_SCHEME)) {
48 // HTTP URLs must be absolute
49 this.url = new URL(url);
50 this.localFile = null;
51 this.cachedFile = mapRemoteURL(this.url);
52 } else {
53 // A path with no scheme means resolve relative to the base URL
54 if (baseUrl.startsWith(FILE_SCHEME)) {
55 this.localFile = resolveFile(baseUrl.substring(FILE_SCHEME.length()), url);
56 this.url = localFile.toURI().toURL();
57 this.cachedFile = null;
58 } else {
59 URL base = new URL(baseUrl);
60 this.url = new URL(base, url);
61 this.localFile = null;
62 this.cachedFile = mapRemoteURL(this.url);
63 }
64 }
65 }
66
67 File resolveFile(String baseFileName, String fileName) {
68 File resolved;
69
70 File baseFile = new File(baseFileName);
71 if (baseFile.isDirectory())
72 resolved = new File(baseFile, fileName);
73 else if (baseFile.isFile())
74 resolved = new File(baseFile.getParentFile(), fileName);
75 else
76 throw new IllegalArgumentException("Cannot resolve relative to non-existant base file path: " + baseFileName);
77
78 return resolved;
79 }
80
81 private File mapRemoteURL(URL url) throws UnsupportedEncodingException {
82 String encoded = URLEncoder.encode(url.toString(), "UTF-8");
83 return new File(cacheDir, encoded);
84 }
85
86 public String getName() {
87 return url.toString();
88 }
89
90 public Location getLocation() {
91 Location result;
92
93 if (localFile != null)
94 result = Location.local;
95 else if (cachedFile.exists())
96 result = Location.remote_cached;
97 else
98 result = Location.remote;
99
100 return result;
101 }
102
103 public File request() throws IOException {
104 if (localFile != null)
105 return localFile;
106 if (cachedFile == null)
107 throw new IllegalStateException("Invalid URLResourceHandle: both local file and cache file are uninitialised.");
108
109 if (!cachedFile.exists()) {
110 cacheDir.mkdirs();
111 downloadToFile(url, cachedFile);
112 }
113
114 return cachedFile;
115 }
116
117 private static void downloadToFile(URL url, File file) throws IOException {
118 InputStream in = null;
119 OutputStream out = null;
120 try {
121 in = url.openStream();
122 out = new FileOutputStream(file);
123
124 byte[] buf = new byte[1024];
125 for(;;) {
126 int bytes = in.read(buf, 0, 1024);
127 if (bytes < 0) break;
128 out.write(buf, 0, bytes);
129 }
130 } finally {
131 try { if (in != null) in.close(); } catch (IOException e) {};
132 try { if (out != null) in.close(); } catch (IOException e) {};
133 }
134 }
135
136}